Initial attempt at parsing commit requests
This commit is contained in:
282
src/commit_request.hpp
Normal file
282
src/commit_request.hpp
Normal file
@@ -0,0 +1,282 @@
|
||||
#pragma once
|
||||
|
||||
#include "arena_allocator.hpp"
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <weaseljson/weaseljson.h>
|
||||
|
||||
/**
|
||||
* @brief Represents a precondition for a commit request.
|
||||
*/
|
||||
struct Precondition {
|
||||
enum class Type { PointRead, RangeRead };
|
||||
|
||||
Type type;
|
||||
std::optional<uint64_t> version;
|
||||
std::string_view key;
|
||||
std::optional<std::string_view> begin;
|
||||
std::optional<std::string_view> end;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents an operation in a commit request.
|
||||
*/
|
||||
struct Operation {
|
||||
enum class Type { Write, Delete, RangeDelete };
|
||||
|
||||
Type type;
|
||||
std::string_view key;
|
||||
std::optional<std::string_view> value;
|
||||
std::optional<std::string_view> begin;
|
||||
std::optional<std::string_view> end;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a commit request as described in the API specification.
|
||||
*
|
||||
* All string data is stored in the arena allocator to ensure efficient
|
||||
* memory management and ownership.
|
||||
*/
|
||||
class CommitRequest {
|
||||
public:
|
||||
// Parser state
|
||||
enum class ParseState {
|
||||
Root,
|
||||
RequestId,
|
||||
LeaderId,
|
||||
ReadVersion,
|
||||
PreconditionsArray,
|
||||
PreconditionObject,
|
||||
OperationsArray,
|
||||
OperationObject
|
||||
};
|
||||
|
||||
enum class ParseStatus {
|
||||
Incomplete, // Still need more data
|
||||
Complete, // Successfully parsed complete JSON
|
||||
Error // Parse error occurred
|
||||
};
|
||||
|
||||
struct ParserContext {
|
||||
std::stack<ParseState> state_stack;
|
||||
std::string current_key;
|
||||
std::string current_string;
|
||||
std::string current_number;
|
||||
bool in_key = false;
|
||||
bool parse_error = false;
|
||||
bool parse_complete = false;
|
||||
|
||||
// Current objects being parsed
|
||||
Precondition current_precondition{};
|
||||
Operation current_operation{};
|
||||
|
||||
// Parsing state for nested structures
|
||||
std::string precondition_type;
|
||||
std::string operation_type;
|
||||
};
|
||||
|
||||
private:
|
||||
ArenaAllocator arena_;
|
||||
std::optional<std::string_view> request_id_;
|
||||
std::string_view leader_id_;
|
||||
uint64_t read_version_ = 0;
|
||||
std::vector<Precondition> preconditions_;
|
||||
std::vector<Operation> operations_;
|
||||
ParserContext parser_context_;
|
||||
WeaselJsonParser *json_parser_ = nullptr;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new CommitRequest with the given initial arena size.
|
||||
* @param arena_size Initial size for the arena allocator
|
||||
*/
|
||||
explicit CommitRequest(size_t arena_size = 4096) : arena_(arena_size) {}
|
||||
|
||||
/**
|
||||
* @brief Destructor - cleans up any active parser.
|
||||
*/
|
||||
~CommitRequest() {
|
||||
if (json_parser_) {
|
||||
WeaselJsonParser_destroy(json_parser_);
|
||||
}
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
CommitRequest(CommitRequest &&other) noexcept
|
||||
: arena_(std::move(other.arena_)), request_id_(other.request_id_),
|
||||
leader_id_(other.leader_id_), read_version_(other.read_version_),
|
||||
preconditions_(std::move(other.preconditions_)),
|
||||
operations_(std::move(other.operations_)),
|
||||
parser_context_(std::move(other.parser_context_)),
|
||||
json_parser_(other.json_parser_) {
|
||||
other.json_parser_ = nullptr;
|
||||
}
|
||||
|
||||
// Move assignment operator
|
||||
CommitRequest &operator=(CommitRequest &&other) noexcept {
|
||||
if (this != &other) {
|
||||
if (json_parser_) {
|
||||
WeaselJsonParser_destroy(json_parser_);
|
||||
}
|
||||
|
||||
arena_ = std::move(other.arena_);
|
||||
request_id_ = other.request_id_;
|
||||
leader_id_ = other.leader_id_;
|
||||
read_version_ = other.read_version_;
|
||||
preconditions_ = std::move(other.preconditions_);
|
||||
operations_ = std::move(other.operations_);
|
||||
parser_context_ = std::move(other.parser_context_);
|
||||
json_parser_ = other.json_parser_;
|
||||
|
||||
other.json_parser_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Copy constructor and assignment are deleted (not safe with parser state)
|
||||
CommitRequest(const CommitRequest &) = delete;
|
||||
CommitRequest &operator=(const CommitRequest &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Parse a JSON string into a CommitRequest object (one-shot parsing).
|
||||
* @param json_str The JSON string to parse
|
||||
* @return true if parsing succeeded, false otherwise
|
||||
*/
|
||||
bool parse_json(std::string_view json_str);
|
||||
|
||||
/**
|
||||
* @brief Initialize streaming JSON parsing.
|
||||
* @return true if initialization succeeded, false otherwise
|
||||
*/
|
||||
bool begin_streaming_parse();
|
||||
|
||||
/**
|
||||
* @brief Parse additional JSON data incrementally.
|
||||
* @param data Pointer to the data buffer
|
||||
* @param len Length of the data
|
||||
* @return ParseStatus indicating current parse state
|
||||
*/
|
||||
ParseStatus parse_chunk(char *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Finish streaming parse (call when no more data is available).
|
||||
* @return ParseStatus indicating final parse result
|
||||
*/
|
||||
ParseStatus finish_streaming_parse();
|
||||
|
||||
/**
|
||||
* @brief Check if parsing is complete and successful.
|
||||
* @return true if parsing is complete and successful
|
||||
*/
|
||||
bool is_parse_complete() const {
|
||||
return parser_context_.parse_complete && !parser_context_.parse_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if there was a parse error.
|
||||
* @return true if there was a parse error
|
||||
*/
|
||||
bool has_parse_error() const { return parser_context_.parse_error; }
|
||||
|
||||
/**
|
||||
* @brief Get the request ID if present.
|
||||
* @return Optional request ID
|
||||
*/
|
||||
const std::optional<std::string_view> &request_id() const {
|
||||
return request_id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the leader ID.
|
||||
* @return Leader ID string view
|
||||
*/
|
||||
std::string_view leader_id() const { return leader_id_; }
|
||||
|
||||
/**
|
||||
* @brief Get the read version.
|
||||
* @return Read version number
|
||||
*/
|
||||
uint64_t read_version() const { return read_version_; }
|
||||
|
||||
/**
|
||||
* @brief Get the preconditions.
|
||||
* @return Vector of preconditions
|
||||
*/
|
||||
const std::vector<Precondition> &preconditions() const {
|
||||
return preconditions_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the operations.
|
||||
* @return Vector of operations
|
||||
*/
|
||||
const std::vector<Operation> &operations() const { return operations_; }
|
||||
|
||||
/**
|
||||
* @brief Get the total allocated bytes in the arena.
|
||||
* @return Total allocated bytes
|
||||
*/
|
||||
size_t total_allocated() const { return arena_.total_allocated(); }
|
||||
|
||||
/**
|
||||
* @brief Get the used bytes in the arena.
|
||||
* @return Used bytes
|
||||
*/
|
||||
size_t used_bytes() const { return arena_.used_bytes(); }
|
||||
|
||||
/**
|
||||
* @brief Reset the commit request for reuse.
|
||||
*/
|
||||
void reset() {
|
||||
arena_.reset();
|
||||
request_id_.reset();
|
||||
leader_id_ = {};
|
||||
read_version_ = 0;
|
||||
preconditions_.clear();
|
||||
operations_.clear();
|
||||
|
||||
// Reset parser state
|
||||
if (json_parser_) {
|
||||
WeaselJsonParser_destroy(json_parser_);
|
||||
json_parser_ = nullptr;
|
||||
}
|
||||
parser_context_ = ParserContext{};
|
||||
parser_context_.state_stack.push(ParseState::Root);
|
||||
}
|
||||
|
||||
// Weaseljson callbacks (public for global callbacks)
|
||||
static void on_begin_object(void *userdata);
|
||||
static void on_end_object(void *userdata);
|
||||
static void on_string_data(void *userdata, const char *buf, int len,
|
||||
int done);
|
||||
static void on_key_data(void *userdata, const char *buf, int len, int done);
|
||||
static void on_begin_array(void *userdata);
|
||||
static void on_end_array(void *userdata);
|
||||
static void on_number_data(void *userdata, const char *buf, int len,
|
||||
int done);
|
||||
static void on_true_literal(void *userdata);
|
||||
static void on_false_literal(void *userdata);
|
||||
static void on_null_literal(void *userdata);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Copy a string into the arena and return a string_view.
|
||||
* @param str The string to copy
|
||||
* @return String view pointing to arena-allocated memory
|
||||
*/
|
||||
std::string_view store_string(std::string_view str);
|
||||
|
||||
/**
|
||||
* @brief Decode a base64 string and store it in the arena.
|
||||
* @param base64_str The base64 encoded string
|
||||
* @return String view of decoded data, or empty view if decoding failed
|
||||
*/
|
||||
std::string_view decode_base64(std::string_view base64_str);
|
||||
|
||||
void handle_completed_string();
|
||||
void handle_completed_number();
|
||||
void handle_completed_key();
|
||||
};
|
||||
Reference in New Issue
Block a user