diff --git a/src/ffi.cpp b/src/ffi.cpp index a25dc8a..6361c53 100644 --- a/src/ffi.cpp +++ b/src/ffi.cpp @@ -24,83 +24,118 @@ void qualify_input_list(std::string prefix, json& in, json& in1); // ------------------------------------------------------------- // ---- File-based entry point (wraps circom-generated main) ---- - -static status::Status validate_generate_witness_from_files_input(const char* dat, const char* inputs, const char* output) { - if (dat == nullptr) { - return status::Status{status::StatusCode::InvalidInput, "dat is null"}; +template +static Status exceptions_into_status(T&& func) { + try { + return func(); + } catch (const std::bad_alloc&) { + return status_from_code(StatusCode_OutOfMemory); + } catch (const std::exception& e) { + return Status{StatusCode_DynError, e.what()}; + } catch (...) { + return Status{StatusCode_DynError, "An unknown error occurred."}; } - if (inputs == nullptr) { - return status::Status{status::StatusCode::InvalidInput, "inputs is null"}; - } - if (output == nullptr) { - return status::Status{status::StatusCode::InvalidInput, "output is null"}; - } - return status::ok(); } -extern "C" status::Status generate_witness_from_files(const char* dat, const char* inputs, const char* output) { - const status::Status status = validate_generate_witness_from_files_input(dat, inputs, output); - if (is_error(status)) { - return status; +static Status validate_generate_witness_from_files_input(const char* dat, const char* inputs, const char* output) { + if (dat == nullptr) { + return Status{StatusCode_InvalidInput, "dat is null"}; } + if (inputs == nullptr) { + return Status{StatusCode_InvalidInput, "inputs is null"}; + } + if (output == nullptr) { + return Status{StatusCode_InvalidInput, "output is null"}; + } + return status_ok(); +} +static Status generate_witness_from_files_impl(const char* dat, const char* inputs, const char* output) { char* argv[] = { + const_cast("generate_witness_from_files"), const_cast(dat), const_cast(inputs), const_cast(output), nullptr }; - const int code = main(3, argv); + const int code = main(4, argv); if (code == 0) { - return status::ok(); + return status_ok(); } - return status::from_code(status::StatusCode::DynError); + return status_from_code(StatusCode_DynError); +} + +extern "C" Status generate_witness_from_files(const char* dat, const char* inputs, const char* output) { + const Status status = validate_generate_witness_from_files_input(dat, inputs, output); + if (status_is_error(status)) { + return status; + } + + return exceptions_into_status([&] { + return generate_witness_from_files_impl(dat, inputs, output); + }); } // ---- Memory-based entry point ---- -static status::Status validate_witness_input(const WitnessInput* input, const Bytes* output) { +static Status validate_witness_input(const WitnessInput* input, const Bytes* output) { if (output == nullptr) { - return status::Status{status::StatusCode::InvalidInput, "output is null"}; + return Status{StatusCode_InvalidInput, "output is null"}; } if (output->data != nullptr) { - return status::Status{status::StatusCode::InvalidInput, "output.data is not null"}; + return Status{StatusCode_InvalidInput, "output.data is not null"}; } if (input == nullptr) { - return status::Status{status::StatusCode::InvalidInput, "input is null"}; + return Status{StatusCode_InvalidInput, "input is null"}; } if (input->dat.data == nullptr) { - return status::Status{status::StatusCode::InvalidInput, "input.dat.data is null"}; + return Status{StatusCode_InvalidInput, "input.dat.data is null"}; } if (input->dat.size == 0) { - return status::Status{status::StatusCode::InvalidInput, "input.dat.size is zero"}; + return Status{StatusCode_InvalidInput, "input.dat.size is zero"}; } if (input->inputs_json == nullptr) { - return status::Status{status::StatusCode::InvalidInput, "input.inputs_json is null"}; + return Status{StatusCode_InvalidInput, "input.inputs_json is null"}; } - return status::ok(); + return status_ok(); } -extern "C" status::Status generate_witness(const WitnessInput* input, Bytes* output) { - const status::Status status = validate_witness_input(input, output); - if (is_error(status)) { - return status; - } - +static Status generate_witness_impl(const WitnessInput* input, Bytes* output) { // TODO: Implement the actual witness generation logic using the provided input data. const uint8_t dummy_witness[] = {0, 1, 2, 3}; // Placeholder for actual witness data const size_t witness_size = sizeof(dummy_witness); uint8_t* witness_data = static_cast(malloc(witness_size)); if (witness_data == nullptr) { - return status::Status{status::StatusCode::OutOfMemory, "Failed to allocate witness memory"}; + return Status{StatusCode_OutOfMemory, "Failed to allocate witness memory"}; } std::copy(dummy_witness, dummy_witness + witness_size, witness_data); output->data = witness_data; output->size = witness_size; - return status::ok(); + return status_ok(); +} + +extern "C" Status generate_witness(const WitnessInput* input, Bytes* output) { + const Status status = validate_witness_input(input, output); + if (status_is_error(status)) { + return status; + } + + return exceptions_into_status([&] { + return generate_witness_impl(input, output); + }); +} + +extern "C" void free_bytes(Bytes* bytes) { + if (bytes == nullptr) { + return; + } + + free(bytes->data); + bytes->data = nullptr; + bytes->size = 0; } diff --git a/src/ffi.hpp b/src/ffi.hpp index ccdf06b..81b1978 100644 --- a/src/ffi.hpp +++ b/src/ffi.hpp @@ -4,12 +4,12 @@ #include "types.hpp" /// Inputs for witness generation. -struct WitnessInput { +typedef struct WitnessInput { /// Contents of the circuit's .dat file. const ConstBytes dat; /// Null-terminated JSON string of circuit inputs. const char* inputs_json; -}; +} WitnessInput; #ifdef __cplusplus extern "C" { @@ -25,9 +25,9 @@ extern "C" { /// /// # Returns /// -/// On success, returns a `Status` with `StatusCode::Ok` and writes the witness to the specified output file. +/// On success, returns a `Status` with `StatusCode_Ok` and writes the witness to the specified output file. /// On failure, returns a `Status` with an appropriate error code. -status::Status generate_witness_from_files(const char* dat, const char* inputs, const char* output); +Status generate_witness_from_files(const char* dat, const char* inputs, const char* output); /// Generates a witness from in-memory buffers. /// @@ -38,10 +38,12 @@ status::Status generate_witness_from_files(const char* dat, const char* inputs, /// /// # Returns /// -/// On success, returns a `Status` with `StatusCode::Ok` and populates `output` with the generated witness bytes. The -/// caller is responsible for freeing the memory allocated for `output.data`. +/// On success, returns a `Status` with `StatusCode_Ok` and populates `output` with the generated witness bytes. The +/// caller is responsible for freeing the resources allocated into `output` by this function using `free_bytes`. /// On failure, returns a `Status` with an appropriate error code, and `output` will not be modified. -status::Status generate_witness(const WitnessInput* input, Bytes* output); +Status generate_witness(const WitnessInput* input, Bytes* output); + +void free_bytes(Bytes* bytes); #ifdef __cplusplus } diff --git a/src/types.hpp b/src/types.hpp index 1ed888a..f0a211c 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -1,57 +1,54 @@ #ifndef TYPES_HPP #define TYPES_HPP -#include -#include +#include +#include -/// A pointer to a contiguous sequence of elements with a known length. -/// -/// # Parameters -/// -/// - `T`: The element type. Use `const T` for immutable data. -template -struct Slice { - /// Pointer to the first element. - T* data; - /// Number of elements. - size_t size; -}; +#ifdef __cplusplus +extern "C" { +#endif /// Mutable byte buffer. -using Bytes = Slice; +typedef struct Bytes { + uint8_t* data; + size_t size; +} Bytes; /// Immutable byte buffer. -using ConstBytes = Slice; +typedef struct ConstBytes { + const uint8_t* data; + size_t size; +} ConstBytes; -namespace status { - /// Represents the outcome of an operation. - enum StatusCode { - Ok = 0, - DynError = 1, - InvalidInput = 2, - OutOfMemory = 3, - }; - - inline bool is_ok(const StatusCode code) { - return code == Ok; - } - - inline bool is_error(const StatusCode code) { - return !is_ok(code); - } - - /// A status code with an optional human-readable description. - struct Status { - StatusCode code; - const char* message; - }; - - inline Status from_code(const StatusCode code) { return Status { code, nullptr }; } - inline Status ok() { return from_code(Ok); } - - inline bool is_ok(const Status status) { return is_ok(status.code); } - inline bool is_error(const Status status) { return is_error(status.code); } +typedef enum StatusCode { + StatusCode_Ok = 0, + StatusCode_DynError = 1, + StatusCode_InvalidInput = 2, + StatusCode_OutOfMemory = 3, +} StatusCode; +static bool status_code_is_ok(const StatusCode code) { + return code == StatusCode_Ok; } +static bool status_code_is_error(const StatusCode code) { + return !status_code_is_ok(code); +} + +/// A status code with an optional human-readable description. +typedef struct Status { + StatusCode code; + const char* message; +} Status; + +static Status status_from_code(const StatusCode code) { return Status { code, NULL }; } +static Status status_ok() { return status_from_code(StatusCode_Ok); } + +static bool status_is_ok(const Status status) { return status_code_is_ok(status.code); } +static bool status_is_error(const Status status) { return status_code_is_error(status.code); } + +#ifdef __cplusplus +} +#endif + #endif // TYPES_HPP