diff --git a/src/Storage.cpp b/src/Storage.cpp index 24fd77c0c7..5e3ed5381c 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -236,10 +236,21 @@ bool Storage::LoadPersistent(std::istream& is, void (*warn)(std::size_t line, const char* msg)) { std::string line_str; std::size_t line_num = 1; - std::string name; + + // declare these outside the loop to reduce reallocs + std::string name, str; + std::vector boolean_array; + std::vector double_array; + std::vector string_array; + + // ignore blank lines and lines that start with ; or # (comments) + while (std::getline(is, line_str)) { + llvm::StringRef line = llvm::StringRef(line_str).trim(); + if (!line.empty() && line.front() != ';' && line.front() != '#') + break; + } // header - std::getline(is, line_str); if (line_str != "[NetworkTables Storage 3.0]") { if (warn) warn(line_num, "header line mismatch, ignoring rest of file"); return false; @@ -249,6 +260,10 @@ bool Storage::LoadPersistent(std::istream& is, llvm::StringRef line = llvm::StringRef(line_str).trim(); ++line_num; + // ignore blank lines and lines that start with ; or # (comments) + if (line.empty() || line.front() == ';' || line.front() == '#') + continue; + // type llvm::StringRef type_tok; std::tie(type_tok, line) = line.split(' '); @@ -294,50 +309,124 @@ bool Storage::LoadPersistent(std::istream& is, StorageEntry entry; switch (type) { case NT_BOOLEAN: - //os << (v.data.v_boolean ? "true" : "false"); - //if (line == "true") - //entry.value. + // only true or false is accepted + if (line == "true") + entry.value().SetBoolean(true); + else if (line == "false") + entry.value().SetBoolean(false); + else { + if (warn) + warn(line_num, "unrecognized boolean value, not 'true' or 'false'"); + goto next_line; + } break; - case NT_DOUBLE: - //os << v.data.v_double; - break; - case NT_STRING: - //WriteString(os, MakeStringRef(v.data.v_string)); - break; - case NT_RAW: { - //char* buf = new char[Base64EncodeLen(v.data.v_raw.len)]; - //Base64Encode(buf, - //reinterpret_cast(v.data.v_raw.str), - //v.data.v_raw.len); - //os << buf; - //delete[] buf; + case NT_DOUBLE: { + // need to convert to null-terminated string for strtod() + str.clear(); + str += line; + char* end; + double v = std::strtod(str.c_str(), &end); + if (*end != '\0') { + if (warn) warn(line_num, "invalid double value"); + goto next_line; + } + entry.value().SetDouble(v); break; } - case NT_BOOLEAN_ARRAY: - //for (size_t i = 0; i < v.data.arr_boolean.size; ++i) { - //os << (v.data.arr_boolean.arr[i] ? "true" : "false"); - //if (i != (v.data.arr_boolean.size - 1)) - //os << ','; - //} + case NT_STRING: { + llvm::StringRef str_tok; + std::tie(str_tok, line) = ReadStringToken(line); + if (str_tok.empty()) { + if (warn) warn(line_num, "missing string value"); + goto next_line; + } + if (str_tok.back() != '"') { + if (warn) warn(line_num, "unterminated string value"); + goto next_line; + } + UnescapeString(str_tok, &str); + entry.value().SetString(str); break; - case NT_DOUBLE_ARRAY: - //for (size_t i = 0; i < v.data.arr_double.size; ++i) { - //os << v.data.arr_double.arr[i]; - //if (i != (v.data.arr_double.size - 1)) - //os << ','; - //} + } + case NT_RAW: + Base64Decode(line, &str); + entry.value().SetRaw(str); break; - case NT_STRING_ARRAY: - //for (size_t i = 0; i < v.data.arr_double.size; ++i) { - //WriteString(os, MakeStringRef(v.data.arr_string.arr[i])); - //if (i != (v.data.arr_double.size - 1)) - //os << ','; - //} + case NT_BOOLEAN_ARRAY: { + llvm::StringRef elem_tok; + boolean_array.clear(); + while (!line.empty()) { + std::tie(elem_tok, line) = line.split(','); + elem_tok = elem_tok.trim(" \t"); + if (elem_tok == "true") + boolean_array.push_back(1); + else if (elem_tok == "false") + boolean_array.push_back(0); + else { + if (warn) + warn(line_num, + "unrecognized boolean value, not 'true' or 'false'"); + goto next_line; + } + } + + entry.value().SetBooleanArray(boolean_array); break; + } + case NT_DOUBLE_ARRAY: { + llvm::StringRef elem_tok; + double_array.clear(); + while (!line.empty()) { + std::tie(elem_tok, line) = line.split(','); + elem_tok = elem_tok.trim(" \t"); + // need to convert to null-terminated string for strtod() + str.clear(); + str += elem_tok; + char* end; + double v = std::strtod(str.c_str(), &end); + if (*end != '\0') { + if (warn) warn(line_num, "invalid double value"); + goto next_line; + } + double_array.push_back(v); + } + + entry.value().SetDoubleArray(double_array); + break; + } + case NT_STRING_ARRAY: { + llvm::StringRef elem_tok; + double_array.clear(); + while (!line.empty()) { + std::tie(elem_tok, line) = ReadStringToken(line); + if (elem_tok.empty()) { + if (warn) warn(line_num, "missing string value"); + goto next_line; + } + if (elem_tok.back() != '"') { + if (warn) warn(line_num, "unterminated string value"); + goto next_line; + } + line = line.ltrim(" \t"); + if (line.empty()) break; + if (line.front() != ',') { + if (warn) warn(line_num, "expected comma between strings"); + goto next_line; + } + line = line.drop_front().ltrim(" \t"); + + UnescapeString(elem_tok, &str); + string_array.push_back(StringValue(str)); + } + + entry.value().SetStringArray(string_array); + break; + } default: break; } - +next_line: + ; } return true; } diff --git a/src/Value.cpp b/src/Value.cpp index 0d9072b843..4717da0c73 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -8,10 +8,94 @@ #include "Value.h" #include +#include #include #include "ntcore.h" +using namespace ntimpl; + +StringValue::StringValue(llvm::StringRef val) { + len = val.size(); + str = static_cast(std::malloc(len+1)); + std::memcpy(str, val.data(), len); + str[len] = '\0'; +} + +void Value::SetBooleanArray(llvm::ArrayRef value) { + // handle type change + if (NT_Value::type != NT_BOOLEAN_ARRAY) { + NT_DisposeValue(this); + data.arr_boolean.arr = nullptr; + data.arr_boolean.size = 0; // set to zero so size change is hit below + NT_Value::type = NT_BOOLEAN_ARRAY; + } + // handle size change + if (data.arr_boolean.size != value.size()) { + std::free(data.arr_boolean.arr); + data.arr_boolean.arr = + static_cast(std::malloc(value.size()*sizeof(int))); + data.arr_boolean.size = value.size(); + } + std::copy(value.begin(), value.end(), data.arr_boolean.arr); +} + +void Value::SetBooleanArray(llvm::ArrayRef value) { + // handle type change + if (NT_Value::type != NT_BOOLEAN_ARRAY) { + NT_DisposeValue(this); + data.arr_boolean.arr = nullptr; + data.arr_boolean.size = 0; // set to zero so size change is hit below + NT_Value::type = NT_BOOLEAN_ARRAY; + } + // handle size change + if (data.arr_boolean.size != value.size()) { + std::free(data.arr_boolean.arr); + data.arr_boolean.arr = + static_cast(std::malloc(value.size()*sizeof(int))); + data.arr_boolean.size = value.size(); + } + std::copy(value.begin(), value.end(), data.arr_boolean.arr); +} + +void Value::SetDoubleArray(llvm::ArrayRef value) { + // handle type change + if (NT_Value::type != NT_DOUBLE_ARRAY) { + NT_DisposeValue(this); + data.arr_double.arr = nullptr; + data.arr_double.size = 0; // set to zero so size change is hit below + NT_Value::type = NT_DOUBLE_ARRAY; + } + // handle size change + if (data.arr_double.size != value.size()) { + std::free(data.arr_double.arr); + data.arr_double.arr = + static_cast(std::malloc(value.size()*sizeof(double))); + data.arr_double.size = value.size(); + } + std::copy(value.begin(), value.end(), data.arr_double.arr); +} + +void Value::SetStringArray(std::vector& value) { + // handle type change + if (NT_Value::type != NT_STRING_ARRAY) { + NT_DisposeValue(this); + data.arr_string.arr = nullptr; + data.arr_string.size = 0; // set to zero so size change is hit below + NT_Value::type = NT_STRING_ARRAY; + } + // handle size change + if (data.arr_string.size != value.size()) { + std::free(data.arr_string.arr); + data.arr_string.arr = + static_cast(std::malloc(value.size()*sizeof(NT_String))); + data.arr_string.size = value.size(); + } + std::move(value.begin(), value.end(), + static_cast(data.arr_string.arr)); + value.clear(); +} + bool ntimpl::operator==(const Value& lhs, const Value& rhs) { if (lhs.type() != rhs.type()) return false; switch (lhs.type()) { diff --git a/src/Value.h b/src/Value.h index b8b2049d9e..c1c4019caa 100644 --- a/src/Value.h +++ b/src/Value.h @@ -9,6 +9,7 @@ #define NT_VALUE_H_ #include +#include #include "ntcore.h" @@ -27,12 +28,31 @@ class StringValue : private NT_String { friend class Value; public: StringValue() { NT_InitString(this); } + /*implicit*/ StringValue(llvm::StringRef val); ~StringValue() { NT_DisposeString(this); } operator llvm::StringRef() const { return llvm::StringRef(str, len); } StringValue(const StringValue&) = delete; StringValue& operator=(const StringValue&) = delete; + + StringValue(StringValue&& other) { + str = other.str; + len = other.len; + other.str = nullptr; + other.len = 0; + } + + StringValue& operator=(StringValue&& other) { + if (this != &other) { + NT_DisposeString(this); + str = other.str; + len = other.len; + other.str = nullptr; + other.len = 0; + } + return *this; + } }; /* @@ -85,26 +105,71 @@ class Value : private NT_Value { /* * Type-Safe Setters */ - void SetBoolean(bool value); - void SetDouble(double value); - void SetString(llvm::StringRef value); - void SetRaw(llvm::StringRef value); - - template - void SetBooleanArray(It begin, It end) { + void SetBoolean(bool value) { + if (NT_Value::type != NT_BOOLEAN) { + NT_DisposeValue(this); + NT_Value::type = NT_BOOLEAN; + } + data.v_boolean = value ? 1 : 0; + } + void SetDouble(double value) { + if (NT_Value::type != NT_DOUBLE) { + NT_DisposeValue(this); + NT_Value::type = NT_DOUBLE; + } + data.v_double = value; + } + void SetString(llvm::StringRef value) { SetString(StringValue(value)); } + void SetString(StringValue&& value) { + if (NT_Value::type != NT_STRING) { + NT_DisposeValue(this); + data.v_string.str = nullptr; + data.v_string.len = 0; + NT_Value::type = NT_STRING; + } + data.v_string = value; + } + void SetRaw(llvm::StringRef value) { SetRaw(StringValue(value)); } + void SetRaw(StringValue&& value) { + if (NT_Value::type != NT_RAW) { + NT_DisposeValue(this); + data.v_raw.str = nullptr; + data.v_raw.len = 0; + NT_Value::type = NT_RAW; + } + data.v_raw = value; } - template - void SetDoubleArray(It begin, It end) { - } + void SetBooleanArray(llvm::ArrayRef value); + void SetBooleanArray(llvm::ArrayRef value); + void SetDoubleArray(llvm::ArrayRef value); - template - void SetStringArray(It begin, It end) { - } + // Note: This function moves the values out of the vector. + void SetStringArray(std::vector& value); Value(const Value&) = delete; Value& operator=(const Value&) = delete; + Value(Value&& other) { + NT_Value::type = static_cast(other).type; + last_change = other.last_change; + data = other.data; + static_cast(other).type = NT_UNASSIGNED; + other.last_change = 0; + } + + Value& operator=(Value&& other) { + if (this != &other) { + NT_DisposeValue(this); + NT_Value::type = static_cast(other).type; + last_change = other.last_change; + data = other.data; + static_cast(other).type = NT_UNASSIGNED; + other.last_change = 0; + } + return *this; + } + friend bool operator==(const Value& lhs, const Value& rhs); };