/* SPDX-License-Identifier: GPL-3.0-or-later */

#ifndef REGEX_TOOLS_PCRE2_BRIDGE_H
#define REGEX_TOOLS_PCRE2_BRIDGE_H

#include <stdint.h>

#define REGEX_PCRE2_BRIDGE_ABI_VERSION 3u
#define REGEX_PCRE2_MAX_PATTERN_BYTES 1048576u
#define REGEX_PCRE2_MAX_SUBJECT_BYTES 16777216u
#define REGEX_PCRE2_MAX_REPLACEMENT_BYTES 262144u
#define REGEX_PCRE2_MAX_MATCHES 10000u
#define REGEX_PCRE2_MAX_CAPTURE_ROWS 100000u
#define REGEX_PCRE2_MAX_CAPTURE_GROUPS 1000u
#define REGEX_PCRE2_MAX_MATCH_LIMIT 100000000u
#define REGEX_PCRE2_MAX_DEPTH_LIMIT 100000u
#define REGEX_PCRE2_MAX_HEAP_LIMIT_KIB 131072u
#define REGEX_PCRE2_MAX_TRACE_EVENTS 50000u
#define REGEX_PCRE2_MAX_TRACE_BYTES 10485760u
#define REGEX_PCRE2_MAX_TRACE_MARK_BYTES 1024u
#define REGEX_PCRE2_UNSET_OFFSET UINT32_MAX

enum regex_pcre2_flag {
  REGEX_PCRE2_CASELESS = 1u << 0,
  REGEX_PCRE2_MULTILINE = 1u << 1,
  REGEX_PCRE2_DOTALL = 1u << 2,
  REGEX_PCRE2_EXTENDED = 1u << 3,
  REGEX_PCRE2_UNGREEDY = 1u << 4,
  REGEX_PCRE2_UTF = 1u << 5,
  REGEX_PCRE2_UCP = 1u << 6,
  REGEX_PCRE2_DUPNAMES = 1u << 7,
  REGEX_PCRE2_GLOBAL = 1u << 8
};

enum regex_pcre2_config_flag {
  REGEX_PCRE2_CONFIG_UNICODE = 1u << 0,
  REGEX_PCRE2_CONFIG_JIT = 1u << 1
};

enum regex_pcre2_bridge_error {
  REGEX_PCRE2_ERROR_INVALID_ARGUMENT = -10001,
  REGEX_PCRE2_ERROR_UNSUPPORTED_FLAGS = -10002,
  REGEX_PCRE2_ERROR_PATTERN_TOO_LARGE = -10003,
  REGEX_PCRE2_ERROR_CONFIG = -10004,
  REGEX_PCRE2_ERROR_SUBJECT_TOO_LARGE = -10005,
  REGEX_PCRE2_ERROR_REPLACEMENT_TOO_LARGE = -10006,
  REGEX_PCRE2_ERROR_LIMIT_OUT_OF_RANGE = -10007,
  REGEX_PCRE2_ERROR_CAPTURE_LIMIT = -10008,
  REGEX_PCRE2_ERROR_OUTPUT_BUFFER = -10009
};

enum regex_pcre2_error_phase {
  REGEX_PCRE2_PHASE_NONE = 0,
  REGEX_PCRE2_PHASE_BRIDGE = 1,
  REGEX_PCRE2_PHASE_COMPILE = 2,
  REGEX_PCRE2_PHASE_MATCH = 3,
  REGEX_PCRE2_PHASE_SUBSTITUTE = 4
};

/*
 * This 20-byte, five-u32 record is retained for deterministic compile-only
 * build verification. Runtime callers should use the bounded APIs below.
 */
typedef struct regex_pcre2_compile_result {
  int32_t status;
  uint32_t error_offset;
  uint32_t capture_count;
  uint32_t name_count;
  uint32_t effective_options;
} regex_pcre2_compile_result;

/* All values are required and validated against the bridge hard maxima. */
typedef struct regex_pcre2_limits {
  uint32_t maximum_matches;
  uint32_t maximum_capture_rows;
  uint32_t match_limit;
  uint32_t depth_limit;
  uint32_t heap_limit_kib;
} regex_pcre2_limits;

/*
 * One record is emitted for group zero and every capture group of each
 * retained match. Unset captures use REGEX_PCRE2_UNSET_OFFSET for both bounds.
 */
typedef struct regex_pcre2_match_record {
  uint32_t match_number;
  uint32_t group_number;
  uint32_t start_byte;
  uint32_t end_byte;
} regex_pcre2_match_record;

/* Names are UTF-8 slices into the caller-owned name byte buffer. */
typedef struct regex_pcre2_name_record {
  uint32_t group_number;
  uint32_t name_offset;
  uint32_t name_length;
} regex_pcre2_name_record;

/*
 * Trace collection has an independent bound from match materialization. The
 * byte cap covers the fixed event records plus copied mark bytes.
 */
typedef struct regex_pcre2_trace_limits {
  uint32_t maximum_events;
  uint32_t maximum_trace_bytes;
  uint32_t match_limit;
  uint32_t depth_limit;
  uint32_t heap_limit_kib;
} regex_pcre2_trace_limits;

/*
 * Every field except the mark slice is copied directly from a PCRE2 callout
 * block. Positions and lengths are offsets in the 8-bit UTF-8 pattern or
 * subject supplied to the engine.
 */
typedef struct regex_pcre2_trace_event {
  uint32_t callout_number;
  uint32_t pattern_position_byte;
  uint32_t next_item_length_byte;
  uint32_t subject_position_byte;
  uint32_t capture_top;
  uint32_t capture_last;
  uint32_t mark_offset;
  uint32_t mark_length;
} regex_pcre2_trace_event;

/*
 * A zero status means that a bounded trace was returned. native_match_status
 * retains pcre2_match()'s exact result, including PCRE2_ERROR_CALLOUT when the
 * bridge deliberately stopped at a trace cap. last_complete_event is
 * UINT32_MAX when no complete event was copied.
 */
typedef struct regex_pcre2_trace_result {
  int32_t status;
  uint32_t error_phase;
  uint32_t error_offset;
  int32_t native_match_status;
  uint32_t effective_options;
  uint32_t event_count;
  uint32_t total_event_count;
  uint32_t mark_bytes_length;
  uint32_t trace_bytes_length;
  uint32_t events_truncated;
  uint32_t marks_truncated;
  uint32_t last_complete_event;
  uint32_t matched;
} regex_pcre2_trace_result;

/*
 * This fixed 60-byte record contains no native pointers. `status` is zero on
 * success. Otherwise `error_phase` distinguishes bridge validation, compile,
 * match and replacement errors; `error_offset` is a UTF-8 byte offset when the
 * phase provides one.
 */
typedef struct regex_pcre2_run_result {
  int32_t status;
  uint32_t error_phase;
  uint32_t error_offset;
  uint32_t capture_count;
  uint32_t name_count;
  uint32_t match_count;
  uint32_t record_count;
  uint32_t name_record_count;
  uint32_t name_bytes_length;
  uint32_t effective_options;
  uint32_t results_truncated;
  uint32_t names_truncated;
  uint32_t output_length;
  uint32_t substitution_count;
  uint32_t output_truncated;
} regex_pcre2_run_result;

uint32_t regex_pcre2_bridge_abi_version(void);
uint32_t regex_pcre2_config_flags(void);
const uint8_t *regex_pcre2_version(void);

int32_t regex_pcre2_error_message(int32_t error_code,
                                  uint8_t *output,
                                  uint32_t output_capacity);

int32_t regex_pcre2_compile_probe(const uint8_t *pattern,
                                  uint32_t pattern_length,
                                  uint32_t application_flags,
                                  regex_pcre2_compile_result *result);

int32_t regex_pcre2_execute(
    const uint8_t *pattern,
    uint32_t pattern_length,
    const uint8_t *subject,
    uint32_t subject_length,
    uint32_t application_flags,
    const regex_pcre2_limits *limits,
    regex_pcre2_match_record *records,
    uint32_t record_capacity,
    regex_pcre2_name_record *name_records,
    uint32_t name_record_capacity,
    uint8_t *name_bytes,
    uint32_t name_bytes_capacity,
    regex_pcre2_run_result *result);

int32_t regex_pcre2_substitute(
    const uint8_t *pattern,
    uint32_t pattern_length,
    const uint8_t *subject,
    uint32_t subject_length,
    const uint8_t *replacement,
    uint32_t replacement_length,
    uint32_t application_flags,
    const regex_pcre2_limits *limits,
    regex_pcre2_match_record *records,
    uint32_t record_capacity,
    regex_pcre2_name_record *name_records,
    uint32_t name_record_capacity,
    uint8_t *name_bytes,
    uint32_t name_bytes_capacity,
    /* `output` must have output_capacity + 1 writable bytes for PCRE2's NUL. */
    uint8_t *output,
    uint32_t output_capacity,
    regex_pcre2_run_result *result);

int32_t regex_pcre2_trace(
    const uint8_t *pattern,
    uint32_t pattern_length,
    const uint8_t *subject,
    uint32_t subject_length,
    uint32_t application_flags,
    const regex_pcre2_trace_limits *limits,
    regex_pcre2_trace_event *events,
    uint32_t event_capacity,
    uint8_t *mark_bytes,
    uint32_t mark_bytes_capacity,
    regex_pcre2_trace_result *result);

int32_t regex_pcre2_self_test(void);

#endif
