WIP making Operation and Precondition smaller

This commit is contained in:
2025-08-14 16:15:14 -04:00
parent 176922e394
commit 7ca50350e7
4 changed files with 148 additions and 144 deletions

View File

@@ -51,6 +51,15 @@ std::string_view CommitRequest::store_string(std::string_view str) {
return std::string_view(arena_str, str.size());
}
void CommitRequest::on_complete() {
// Fill in default read version
for (auto &precondition : preconditions_) {
if (precondition.version == 0) {
precondition.version = read_version_;
}
}
}
std::string_view CommitRequest::decode_base64(std::string_view base64_str) {
if (base64_str.empty()) {
return {};
@@ -119,11 +128,11 @@ void CommitRequest::on_begin_object(void *userdata) {
break;
case ParseState::PreconditionsArray:
ctx.state_stack.push(ParseState::PreconditionObject);
ctx.current_precondition = Precondition{};
ctx.current_precondition = PreconditionParseState{};
break;
case ParseState::OperationsArray:
ctx.state_stack.push(ParseState::OperationObject);
ctx.current_operation = Operation{};
ctx.current_operation = OperationParseState{};
break;
default:
ctx.parse_error = true;
@@ -147,12 +156,66 @@ void CommitRequest::on_end_object(void *userdata) {
case ParseState::Root:
// We're done parsing the root object
ctx.parse_complete = true;
self->on_complete();
break;
case ParseState::PreconditionObject:
self->preconditions_.push_back(ctx.current_precondition);
switch (ctx.current_precondition.type) {
case Precondition::Type::PointRead:
if (!ctx.current_precondition.key.has_value()) {
ctx.parse_error = true;
} else {
self->preconditions_.push_back(
Precondition{ctx.current_precondition.type,
ctx.current_precondition.version.value_or(0),
ctx.current_precondition.key.value(),
{}});
}
break;
case Precondition::Type::RangeRead:
if (!ctx.current_precondition.begin.has_value() ||
!ctx.current_precondition.end.has_value()) {
ctx.parse_error = true;
} else {
self->preconditions_.push_back(
Precondition{ctx.current_precondition.type,
ctx.current_precondition.version.value_or(0),
ctx.current_precondition.begin.value(),
ctx.current_precondition.end.value()});
}
break;
}
break;
case ParseState::OperationObject:
self->operations_.push_back(ctx.current_operation);
switch (ctx.current_operation.type) {
case Operation::Type::Write:
if (!ctx.current_operation.key.has_value() ||
!ctx.current_operation.value.has_value()) {
ctx.parse_error = true;
} else {
self->operations_.push_back(Operation{
ctx.current_operation.type, ctx.current_operation.key.value(),
ctx.current_operation.value.value()});
}
break;
case Operation::Type::Delete:
if (!ctx.current_operation.key.has_value()) {
ctx.parse_error = true;
} else {
self->operations_.push_back(Operation{
ctx.current_operation.type, ctx.current_operation.key.value(), {}});
}
break;
case Operation::Type::RangeDelete:
if (!ctx.current_operation.begin.has_value() ||
!ctx.current_operation.end.has_value()) {
ctx.parse_error = true;
} else {
self->operations_.push_back(Operation{
ctx.current_operation.type, ctx.current_operation.begin.value(),
ctx.current_operation.end.value()});
}
break;
}
break;
default:
break;
@@ -361,31 +424,13 @@ void CommitRequest::handle_completed_key() {
// No immediate action needed as we'll use it when processing values
}
bool CommitRequest::parse_json(std::string_view json_str) {
reset();
WeaselJsonParser *parser =
WeaselJsonParser_create(64, &json_callbacks, this, 0);
if (!parser) {
bool CommitRequest::parse_json(char *data, size_t len) {
if (!begin_streaming_parse()) {
return false;
}
// Make a mutable copy of the JSON string for parsing
std::string mutable_json(json_str);
WeaselJsonStatus status =
WeaselJsonParser_parse(parser, mutable_json.data(), mutable_json.size());
if (status == WeaselJson_OK || status == WeaselJson_AGAIN) {
// End of input
status = WeaselJsonParser_parse(parser, nullptr, 0);
}
bool success =
(status == WeaselJson_OK) && !parser_context_.parse_error && is_valid();
WeaselJsonParser_destroy(parser);
return success;
parse_chunk(data, len);
finish_streaming_parse();
return is_parse_complete();
}
bool CommitRequest::begin_streaming_parse() {

View File

@@ -16,10 +16,9 @@ struct Precondition {
enum class Type { PointRead, RangeRead };
Type type;
std::optional<uint64_t> version;
std::optional<std::string_view> key;
std::optional<std::string_view> begin;
std::optional<std::string_view> end;
uint64_t version;
std::string_view begin;
std::string_view end;
};
/**
@@ -29,10 +28,8 @@ struct Operation {
enum class Type { Write, Delete, RangeDelete };
Type type;
std::optional<std::string_view> key;
std::optional<std::string_view> value;
std::optional<std::string_view> begin;
std::optional<std::string_view> end;
std::string_view param1;
std::string_view param2;
};
/**
@@ -42,6 +39,25 @@ struct Operation {
* memory management and ownership.
*/
class CommitRequest {
struct PreconditionParseState {
Precondition::Type type;
std::optional<uint64_t> version;
std::optional<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 OperationParseState {
Operation::Type type;
std::optional<std::string_view> key;
std::optional<std::string_view> value;
std::optional<std::string_view> begin;
std::optional<std::string_view> end;
};
public:
// Parser state
enum class ParseState {
@@ -77,8 +93,8 @@ public:
bool parse_complete = false;
// Current objects being parsed
Precondition current_precondition{};
Operation current_operation{};
PreconditionParseState current_precondition{};
OperationParseState current_operation{};
// Parsing state for nested structures
ArenaString precondition_type;
@@ -115,8 +131,8 @@ public:
*/
explicit CommitRequest(size_t arena_size = 4096)
: arena_(arena_size),
preconditions_(ArenaStlAllocator<Precondition>(&arena_)),
operations_(ArenaStlAllocator<Operation>(&arena_)),
preconditions_(ArenaStlAllocator<PreconditionParseState>(&arena_)),
operations_(ArenaStlAllocator<OperationParseState>(&arena_)),
parser_context_(&arena_) {}
/**
@@ -171,7 +187,7 @@ public:
* @param json_str The JSON string to parse
* @return true if parsing succeeded, false otherwise
*/
bool parse_json(std::string_view json_str);
bool parse_json(char *data, size_t len);
/**
* @brief Initialize streaming JSON parsing.
@@ -199,69 +215,9 @@ public:
*/
bool is_parse_complete() const {
return parser_context_.parse_complete && !parser_context_.parse_error &&
is_valid();
!leader_id_.empty() && has_read_version_been_set_;
}
/**
* @brief Validate that all required fields are present.
* @return true if all required fields are present
*/
bool is_valid() const {
return !leader_id_.empty() && has_read_version_been_set_ &&
validate_preconditions() && validate_operations();
}
private:
/**
* @brief Validate that all preconditions have required fields.
* @return true if all preconditions are valid
*/
bool validate_preconditions() const {
for (const auto &precondition : preconditions_) {
switch (precondition.type) {
case Precondition::Type::PointRead:
if (!precondition.key.has_value()) {
return false; // key is required for point_read
}
break;
case Precondition::Type::RangeRead:
if (!precondition.begin.has_value() || !precondition.end.has_value()) {
return false; // begin and end are required for range_read
}
break;
}
}
return true;
}
/**
* @brief Validate that all operations have required fields.
* @return true if all operations are valid
*/
bool validate_operations() const {
for (const auto &operation : operations_) {
switch (operation.type) {
case Operation::Type::Write:
if (!operation.key.has_value() || !operation.value.has_value()) {
return false; // key and value are required for write
}
break;
case Operation::Type::Delete:
if (!operation.key.has_value()) {
return false; // key is required for delete
}
break;
case Operation::Type::RangeDelete:
if (!operation.begin.has_value() || !operation.end.has_value()) {
return false; // begin and end are required for range_delete
}
break;
}
}
return true;
}
public:
/**
* @brief Check if there was a parse error.
* @return true if there was a parse error
@@ -354,6 +310,8 @@ private:
*/
std::string_view store_string(std::string_view str);
void on_complete();
/**
* @brief Decode a base64 string and store it in the arena.
* @param base64_str The base64 encoded string

View File

@@ -20,8 +20,7 @@ void print_stats(const CommitRequest &request) {
const auto &op = request.operations()[0];
std::cout << " First operation: "
<< (op.type == Operation::Type::Write ? "write" : "other")
<< " key=" << op.key
<< " value=" << (op.value ? op.value.value() : "none")
<< " param1=" << op.param1 << " param2=" << op.param2
<< std::endl;
}
}
@@ -85,7 +84,7 @@ int main(int argc, char *argv[]) {
})";
auto copy = sample_json;
if (request.parse_json(copy)) {
if (request.parse_json(copy.data(), copy.size())) {
print_stats(request);
} else {
std::cout << "✗ Failed to parse commit request" << std::endl;