Files
weaseldb/src/json_commit_request_parser.hpp

160 lines
5.3 KiB
C++

#pragma once
#include <memory>
#include <simdutf.h>
#include <weaseljson/weaseljson.h>
#include "commit_request_parser.hpp"
#include "json_token_enum.hpp"
/**
* @brief JSON-specific implementation of CommitRequestParser.
*
* This parser uses the weaseljson library to parse JSON-formatted
* commit requests into CommitRequest objects.
*/
struct JsonCommitRequestParser : CommitRequestParser {
public:
// Parser state
enum class ParseState {
Root,
PreconditionsArray,
PreconditionObject,
OperationsArray,
OperationObject
};
private:
struct PreconditionParseState {
Precondition::Type type;
std::optional<int64_t> version;
// These are owned by CommitRequest::arena
std::optional<std::string_view> key;
std::optional<std::string_view> begin;
std::optional<std::string_view> end;
};
/**
* @brief Internal state for parsing an operation during JSON processing.
*/
struct OperationParseState {
Operation::Type type;
// These are owned by CommitRequest::arena
std::optional<std::string_view> key;
std::optional<std::string_view> value;
std::optional<std::string_view> begin;
std::optional<std::string_view> end;
};
struct ParserContext {
using ArenaString = std::basic_string<char, std::char_traits<char>,
ArenaStlAllocator<char>>;
ParseState current_state = ParseState::Root;
JsonTokenType current_key_token;
// Only used if we need to accumulate the current key
ArenaString current_key;
ArenaString current_string;
ArenaString current_number;
const char *parse_error = nullptr;
bool parse_complete = false;
bool has_read_version_been_set = false;
// Current objects being parsed
PreconditionParseState current_precondition{};
OperationParseState current_operation{};
// Parsing state for nested structures
ArenaString precondition_type;
ArenaString operation_type;
// Constructor to initialize arena-allocated containers
explicit ParserContext(ArenaAllocator *arena)
: current_key(ArenaStlAllocator<char>(arena)),
current_string(ArenaStlAllocator<char>(arena)),
current_number(ArenaStlAllocator<char>(arena)),
precondition_type(ArenaStlAllocator<char>(arena)),
operation_type(ArenaStlAllocator<char>(arena)) {
has_read_version_been_set = false;
}
void attach_arena(ArenaAllocator *arena) {
current_key = ArenaString{ArenaStlAllocator<char>(arena)};
current_string = ArenaString{ArenaStlAllocator<char>(arena)};
current_number = ArenaString{ArenaStlAllocator<char>(arena)};
current_precondition = {};
current_operation = {};
precondition_type = ArenaString{ArenaStlAllocator<char>(arena)};
operation_type = ArenaString{ArenaStlAllocator<char>(arena)};
current_state = ParseState::Root;
}
};
WeaselJsonParser *json_parser_ = nullptr;
std::unique_ptr<ParserContext> parser_context_;
CommitRequest *current_request_ = nullptr;
static const WeaselJsonCallbacks json_callbacks;
public:
/**
* @brief Construct a new JsonCommitRequestParser.
*/
JsonCommitRequestParser();
/**
* @brief Destructor - cleans up any active parser.
*/
~JsonCommitRequestParser();
// Non-copyable but movable
JsonCommitRequestParser(const JsonCommitRequestParser &) = delete;
JsonCommitRequestParser &operator=(const JsonCommitRequestParser &) = delete;
JsonCommitRequestParser(JsonCommitRequestParser &&other) noexcept;
JsonCommitRequestParser &operator=(JsonCommitRequestParser &&other) noexcept;
// CommitRequestParser interface implementation
ParseResult parse(CommitRequest &request, char *data, int64_t len) override;
bool begin_streaming_parse(CommitRequest &request) override;
ParseStatus parse_chunk(char *data, int64_t len) override;
ParseStatus finish_streaming_parse() override;
const char *get_parse_error() const override;
/**
* @brief Check if read version has been explicitly set during parsing.
*
* @note This function is primarily used for testing to verify parser
* behavior. It exposes internal parser state that is not typically needed in
* production code.
*
* @return true if read version was set during parsing
*/
bool has_read_version_been_set() const;
// 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 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(std::string_view s);
void handle_completed_number(std::string_view s);
void on_complete();
};