Files
allwpilib/ntcore/src/main/native/cpp/Value.cpp
Peter Johnson f84018af5f Move entirety of llvm namespace to wpi namespace.
During shared library loading, a different libLLVM can be pulled in, causing
llvm symbols from dependent libraries to resolve to that library instead of
this one. This has been seen in the wild with the Mesa OpenGL implementation
in JavaFX applications (see wpilibsuite/shuffleboard#361).

This is clearly a very breaking change. For some level of backwards
compatibility, a namespace alias from llvm to wpi is performed in the "llvm"
headers.  Unfortunately, forward declarations of llvm classes will still break,
but compilers seem to generate clear error messages in those cases
("namespace alias 'llvm' not allowed here, assuming 'wpi'").

This change also moves all the wpiutil headers to a single "wpi" subdirectory
from the previously split "llvm", "support", "tcpsockets", and "udpsockets".
Shim headers will be added for backwards compatibility in a later commit.
2018-04-30 10:22:54 -07:00

219 lines
7.8 KiB
C++

/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2015-2018. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include <stdint.h>
#include <wpi/timestamp.h>
#include "Value_internal.h"
#include "networktables/NetworkTableValue.h"
using namespace nt;
Value::Value() {
m_val.type = NT_UNASSIGNED;
m_val.last_change = wpi::Now();
}
Value::Value(NT_Type type, uint64_t time, const private_init&) {
m_val.type = type;
if (time == 0)
m_val.last_change = wpi::Now();
else
m_val.last_change = time;
if (m_val.type == NT_BOOLEAN_ARRAY)
m_val.data.arr_boolean.arr = nullptr;
else if (m_val.type == NT_DOUBLE_ARRAY)
m_val.data.arr_double.arr = nullptr;
else if (m_val.type == NT_STRING_ARRAY)
m_val.data.arr_string.arr = nullptr;
}
Value::~Value() {
if (m_val.type == NT_BOOLEAN_ARRAY)
delete[] m_val.data.arr_boolean.arr;
else if (m_val.type == NT_DOUBLE_ARRAY)
delete[] m_val.data.arr_double.arr;
else if (m_val.type == NT_STRING_ARRAY)
delete[] m_val.data.arr_string.arr;
}
std::shared_ptr<Value> Value::MakeBooleanArray(wpi::ArrayRef<int> value,
uint64_t time) {
auto val = std::make_shared<Value>(NT_BOOLEAN_ARRAY, time, private_init());
val->m_val.data.arr_boolean.arr = new int[value.size()];
val->m_val.data.arr_boolean.size = value.size();
std::copy(value.begin(), value.end(), val->m_val.data.arr_boolean.arr);
return val;
}
std::shared_ptr<Value> Value::MakeDoubleArray(wpi::ArrayRef<double> value,
uint64_t time) {
auto val = std::make_shared<Value>(NT_DOUBLE_ARRAY, time, private_init());
val->m_val.data.arr_double.arr = new double[value.size()];
val->m_val.data.arr_double.size = value.size();
std::copy(value.begin(), value.end(), val->m_val.data.arr_double.arr);
return val;
}
std::shared_ptr<Value> Value::MakeStringArray(wpi::ArrayRef<std::string> value,
uint64_t time) {
auto val = std::make_shared<Value>(NT_STRING_ARRAY, time, private_init());
val->m_string_array = value;
// point NT_Value to the contents in the vector.
val->m_val.data.arr_string.arr = new NT_String[value.size()];
val->m_val.data.arr_string.size = val->m_string_array.size();
for (size_t i = 0; i < value.size(); ++i) {
val->m_val.data.arr_string.arr[i].str = const_cast<char*>(value[i].c_str());
val->m_val.data.arr_string.arr[i].len = value[i].size();
}
return val;
}
std::shared_ptr<Value> Value::MakeStringArray(std::vector<std::string>&& value,
uint64_t time) {
auto val = std::make_shared<Value>(NT_STRING_ARRAY, time, private_init());
val->m_string_array = std::move(value);
value.clear();
// point NT_Value to the contents in the vector.
val->m_val.data.arr_string.arr = new NT_String[val->m_string_array.size()];
val->m_val.data.arr_string.size = val->m_string_array.size();
for (size_t i = 0; i < val->m_string_array.size(); ++i) {
val->m_val.data.arr_string.arr[i].str =
const_cast<char*>(val->m_string_array[i].c_str());
val->m_val.data.arr_string.arr[i].len = val->m_string_array[i].size();
}
return val;
}
void nt::ConvertToC(const Value& in, NT_Value* out) {
out->type = NT_UNASSIGNED;
switch (in.type()) {
case NT_UNASSIGNED:
return;
case NT_BOOLEAN:
out->data.v_boolean = in.GetBoolean() ? 1 : 0;
break;
case NT_DOUBLE:
out->data.v_double = in.GetDouble();
break;
case NT_STRING:
ConvertToC(in.GetString(), &out->data.v_string);
break;
case NT_RAW:
ConvertToC(in.GetRaw(), &out->data.v_raw);
break;
case NT_RPC:
ConvertToC(in.GetRpc(), &out->data.v_raw);
break;
case NT_BOOLEAN_ARRAY: {
auto v = in.GetBooleanArray();
out->data.arr_boolean.arr =
static_cast<int*>(std::malloc(v.size() * sizeof(int)));
out->data.arr_boolean.size = v.size();
std::copy(v.begin(), v.end(), out->data.arr_boolean.arr);
break;
}
case NT_DOUBLE_ARRAY: {
auto v = in.GetDoubleArray();
out->data.arr_double.arr =
static_cast<double*>(std::malloc(v.size() * sizeof(double)));
out->data.arr_double.size = v.size();
std::copy(v.begin(), v.end(), out->data.arr_double.arr);
break;
}
case NT_STRING_ARRAY: {
auto v = in.GetStringArray();
out->data.arr_string.arr =
static_cast<NT_String*>(std::malloc(v.size() * sizeof(NT_String)));
for (size_t i = 0; i < v.size(); ++i)
ConvertToC(v[i], &out->data.arr_string.arr[i]);
out->data.arr_string.size = v.size();
break;
}
default:
// assert(false && "unknown value type");
return;
}
out->type = in.type();
}
void nt::ConvertToC(wpi::StringRef in, NT_String* out) {
out->len = in.size();
out->str = static_cast<char*>(std::malloc(in.size() + 1));
std::memcpy(out->str, in.data(), in.size());
out->str[in.size()] = '\0';
}
std::shared_ptr<Value> nt::ConvertFromC(const NT_Value& value) {
switch (value.type) {
case NT_UNASSIGNED:
return nullptr;
case NT_BOOLEAN:
return Value::MakeBoolean(value.data.v_boolean != 0);
case NT_DOUBLE:
return Value::MakeDouble(value.data.v_double);
case NT_STRING:
return Value::MakeString(ConvertFromC(value.data.v_string));
case NT_RAW:
return Value::MakeRaw(ConvertFromC(value.data.v_raw));
case NT_RPC:
return Value::MakeRpc(ConvertFromC(value.data.v_raw));
case NT_BOOLEAN_ARRAY:
return Value::MakeBooleanArray(wpi::ArrayRef<int>(
value.data.arr_boolean.arr, value.data.arr_boolean.size));
case NT_DOUBLE_ARRAY:
return Value::MakeDoubleArray(wpi::ArrayRef<double>(
value.data.arr_double.arr, value.data.arr_double.size));
case NT_STRING_ARRAY: {
std::vector<std::string> v;
v.reserve(value.data.arr_string.size);
for (size_t i = 0; i < value.data.arr_string.size; ++i)
v.push_back(ConvertFromC(value.data.arr_string.arr[i]));
return Value::MakeStringArray(std::move(v));
}
default:
// assert(false && "unknown value type");
return nullptr;
}
}
bool nt::operator==(const Value& lhs, const Value& rhs) {
if (lhs.type() != rhs.type()) return false;
switch (lhs.type()) {
case NT_UNASSIGNED:
return true; // XXX: is this better being false instead?
case NT_BOOLEAN:
return lhs.m_val.data.v_boolean == rhs.m_val.data.v_boolean;
case NT_DOUBLE:
return lhs.m_val.data.v_double == rhs.m_val.data.v_double;
case NT_STRING:
case NT_RAW:
case NT_RPC:
return lhs.m_string == rhs.m_string;
case NT_BOOLEAN_ARRAY:
if (lhs.m_val.data.arr_boolean.size != rhs.m_val.data.arr_boolean.size)
return false;
return std::memcmp(lhs.m_val.data.arr_boolean.arr,
rhs.m_val.data.arr_boolean.arr,
lhs.m_val.data.arr_boolean.size *
sizeof(lhs.m_val.data.arr_boolean.arr[0])) == 0;
case NT_DOUBLE_ARRAY:
if (lhs.m_val.data.arr_double.size != rhs.m_val.data.arr_double.size)
return false;
return std::memcmp(lhs.m_val.data.arr_double.arr,
rhs.m_val.data.arr_double.arr,
lhs.m_val.data.arr_double.size *
sizeof(lhs.m_val.data.arr_double.arr[0])) == 0;
case NT_STRING_ARRAY:
return lhs.m_string_array == rhs.m_string_array;
default:
// assert(false && "unknown value type");
return false;
}
}