268 lines
8.9 KiB
C++
268 lines
8.9 KiB
C++
#include "nlohmann_reference_parser.hpp"
|
|
#include <simdutf.h>
|
|
|
|
NlohmannReferenceParser::ParseResult
|
|
NlohmannReferenceParser::parse(CommitRequest &request,
|
|
const std::string &json_str) {
|
|
error_message_.clear();
|
|
request.reset();
|
|
|
|
try {
|
|
nlohmann::json j = nlohmann::json::parse(json_str);
|
|
|
|
// Parse required fields
|
|
if (!j.contains("leader_id") || !j["leader_id"].is_string()) {
|
|
error_message_ = "Missing or invalid leader_id";
|
|
return ParseResult::ValidationError;
|
|
}
|
|
|
|
std::string leader_id = j["leader_id"];
|
|
if (leader_id.empty()) {
|
|
error_message_ = "Empty leader_id";
|
|
return ParseResult::ValidationError;
|
|
}
|
|
request.set_leader_id(request.copy_to_arena(leader_id));
|
|
|
|
if (!j.contains("read_version") ||
|
|
!j["read_version"].is_number_unsigned()) {
|
|
error_message_ = "Missing or invalid read_version";
|
|
return ParseResult::ValidationError;
|
|
}
|
|
request.set_read_version(j["read_version"]);
|
|
|
|
// Parse optional request_id
|
|
if (j.contains("request_id")) {
|
|
if (!j["request_id"].is_string()) {
|
|
error_message_ = "Invalid request_id type";
|
|
return ParseResult::ValidationError;
|
|
}
|
|
std::string request_id = j["request_id"];
|
|
request.set_request_id(request.copy_to_arena(request_id));
|
|
}
|
|
|
|
// Parse optional preconditions
|
|
if (j.contains("preconditions")) {
|
|
if (!j["preconditions"].is_array()) {
|
|
error_message_ = "Preconditions must be an array";
|
|
return ParseResult::ValidationError;
|
|
}
|
|
if (!parse_preconditions(request, j["preconditions"])) {
|
|
return ParseResult::ValidationError;
|
|
}
|
|
}
|
|
|
|
// Parse optional operations
|
|
if (j.contains("operations")) {
|
|
if (!j["operations"].is_array()) {
|
|
error_message_ = "Operations must be an array";
|
|
return ParseResult::ValidationError;
|
|
}
|
|
if (!parse_operations(request, j["operations"])) {
|
|
return ParseResult::ValidationError;
|
|
}
|
|
}
|
|
|
|
request.finalize();
|
|
return ParseResult::Success;
|
|
|
|
} catch (const nlohmann::json::parse_error &e) {
|
|
error_message_ = "JSON parse error: " + std::string(e.what());
|
|
return ParseResult::ParseError;
|
|
} catch (const nlohmann::json::exception &e) {
|
|
error_message_ = "JSON error: " + std::string(e.what());
|
|
return ParseResult::ParseError;
|
|
}
|
|
}
|
|
|
|
bool NlohmannReferenceParser::parse_preconditions(
|
|
CommitRequest &request, const nlohmann::json &preconditions_array) {
|
|
for (const auto &precondition : preconditions_array) {
|
|
if (!precondition.is_object()) {
|
|
error_message_ = "Precondition must be an object";
|
|
return false;
|
|
}
|
|
|
|
// Parse type
|
|
if (!precondition.contains("type") || !precondition["type"].is_string()) {
|
|
error_message_ = "Precondition missing type";
|
|
return false;
|
|
}
|
|
|
|
std::string type_str = precondition["type"];
|
|
Precondition::Type type = parse_precondition_type(type_str);
|
|
if (type_str != "point_read" && type_str != "range_read") {
|
|
error_message_ = "Invalid precondition type: " + type_str;
|
|
return false;
|
|
}
|
|
|
|
// Parse version (optional, defaults to 0)
|
|
uint64_t version = 0;
|
|
if (precondition.contains("version")) {
|
|
if (!precondition["version"].is_number_unsigned()) {
|
|
error_message_ = "Invalid precondition version";
|
|
return false;
|
|
}
|
|
version = precondition["version"];
|
|
}
|
|
|
|
if (type == Precondition::Type::PointRead) {
|
|
// Point read requires key field
|
|
if (!precondition.contains("key") || !precondition["key"].is_string()) {
|
|
error_message_ = "Point read precondition missing key";
|
|
return false;
|
|
}
|
|
|
|
std::string key_b64 = precondition["key"];
|
|
std::string key_decoded = decode_base64(key_b64);
|
|
|
|
request.add_precondition(type, version,
|
|
request.copy_to_arena(key_decoded));
|
|
} else { // RangeRead
|
|
// Range read requires begin and end fields
|
|
if (!precondition.contains("begin") ||
|
|
!precondition["begin"].is_string()) {
|
|
error_message_ = "Range read precondition missing begin";
|
|
return false;
|
|
}
|
|
if (!precondition.contains("end") || !precondition["end"].is_string()) {
|
|
error_message_ = "Range read precondition missing end";
|
|
return false;
|
|
}
|
|
|
|
std::string begin_b64 = precondition["begin"];
|
|
std::string end_b64 = precondition["end"];
|
|
std::string begin_decoded = decode_base64(begin_b64);
|
|
std::string end_decoded = decode_base64(end_b64);
|
|
|
|
request.add_precondition(type, version,
|
|
request.copy_to_arena(begin_decoded),
|
|
request.copy_to_arena(end_decoded));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool NlohmannReferenceParser::parse_operations(
|
|
CommitRequest &request, const nlohmann::json &operations_array) {
|
|
for (const auto &operation : operations_array) {
|
|
if (!operation.is_object()) {
|
|
error_message_ = "Operation must be an object";
|
|
return false;
|
|
}
|
|
|
|
// Parse type
|
|
if (!operation.contains("type") || !operation["type"].is_string()) {
|
|
error_message_ = "Operation missing type";
|
|
return false;
|
|
}
|
|
|
|
std::string type_str = operation["type"];
|
|
Operation::Type type = parse_operation_type(type_str);
|
|
if (type_str != "write" && type_str != "delete" &&
|
|
type_str != "range_delete") {
|
|
error_message_ = "Invalid operation type: " + type_str;
|
|
return false;
|
|
}
|
|
|
|
if (type == Operation::Type::Write) {
|
|
// Write requires key and value
|
|
if (!operation.contains("key") || !operation["key"].is_string()) {
|
|
error_message_ = "Write operation missing key";
|
|
return false;
|
|
}
|
|
if (!operation.contains("value") || !operation["value"].is_string()) {
|
|
error_message_ = "Write operation missing value";
|
|
return false;
|
|
}
|
|
|
|
std::string key_b64 = operation["key"];
|
|
std::string value_b64 = operation["value"];
|
|
std::string key_decoded = decode_base64(key_b64);
|
|
std::string value_decoded = decode_base64(value_b64);
|
|
|
|
request.add_operation(type, request.copy_to_arena(key_decoded),
|
|
request.copy_to_arena(value_decoded));
|
|
} else if (type == Operation::Type::Delete) {
|
|
// Delete requires key
|
|
if (!operation.contains("key") || !operation["key"].is_string()) {
|
|
error_message_ = "Delete operation missing key";
|
|
return false;
|
|
}
|
|
|
|
std::string key_b64 = operation["key"];
|
|
std::string key_decoded = decode_base64(key_b64);
|
|
|
|
request.add_operation(type, request.copy_to_arena(key_decoded));
|
|
} else { // RangeDelete
|
|
// Range delete requires begin and end
|
|
if (!operation.contains("begin") || !operation["begin"].is_string()) {
|
|
error_message_ = "Range delete operation missing begin";
|
|
return false;
|
|
}
|
|
if (!operation.contains("end") || !operation["end"].is_string()) {
|
|
error_message_ = "Range delete operation missing end";
|
|
return false;
|
|
}
|
|
|
|
std::string begin_b64 = operation["begin"];
|
|
std::string end_b64 = operation["end"];
|
|
std::string begin_decoded = decode_base64(begin_b64);
|
|
std::string end_decoded = decode_base64(end_b64);
|
|
|
|
request.add_operation(type, request.copy_to_arena(begin_decoded),
|
|
request.copy_to_arena(end_decoded));
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string
|
|
NlohmannReferenceParser::decode_base64(const std::string &base64_str) {
|
|
if (base64_str.empty()) {
|
|
return "";
|
|
}
|
|
|
|
// Calculate required output size for worst case
|
|
size_t max_output_size = simdutf::maximal_binary_length_from_base64(
|
|
base64_str.data(), base64_str.size());
|
|
|
|
// Allocate output buffer
|
|
std::vector<char> output(max_output_size);
|
|
|
|
// Decode using simdutf
|
|
simdutf::result result =
|
|
simdutf::base64_to_binary(base64_str.data(), base64_str.size(),
|
|
output.data(), simdutf::base64_default);
|
|
|
|
if (result.error == simdutf::error_code::SUCCESS) {
|
|
return std::string(output.data(), result.count);
|
|
} else {
|
|
// Return original string if decode fails (for non-base64 strings)
|
|
return base64_str;
|
|
}
|
|
}
|
|
|
|
Precondition::Type
|
|
NlohmannReferenceParser::parse_precondition_type(const std::string &type_str) {
|
|
if (type_str == "point_read") {
|
|
return Precondition::Type::PointRead;
|
|
} else if (type_str == "range_read") {
|
|
return Precondition::Type::RangeRead;
|
|
}
|
|
// Default fallback (should not happen if validation is correct)
|
|
return Precondition::Type::PointRead;
|
|
}
|
|
|
|
Operation::Type
|
|
NlohmannReferenceParser::parse_operation_type(const std::string &type_str) {
|
|
if (type_str == "write") {
|
|
return Operation::Type::Write;
|
|
} else if (type_str == "delete") {
|
|
return Operation::Type::Delete;
|
|
} else if (type_str == "range_delete") {
|
|
return Operation::Type::RangeDelete;
|
|
}
|
|
// Default fallback (should not happen if validation is correct)
|
|
return Operation::Type::Write;
|
|
}
|