Skip to content

Instantly share code, notes, and snippets.

@Vizonex
Created September 17, 2025 22:18
Show Gist options
  • Select an option

  • Save Vizonex/f3f72b38bc2eac17f8fdc625bc6c32a9 to your computer and use it in GitHub Desktop.

Select an option

Save Vizonex/f3f72b38bc2eac17f8fdc625bc6c32a9 to your computer and use it in GitHub Desktop.
Inspired by llhttp this is a http2 frame parser concept for upcoming C Library on github for use with aiohttp and other libraries
#include "api.h"
#include <stdio.h>
// Don't want to override llhttp's work now do we?
#define H2_CALLBACK_MAYBE(PARSER, NAME) \
do { \
const llh2_settings_t *settings; \
settings = (const llh2_settings_t *)(PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) \
{ \
err = 0; \
break; \
} \
err = settings->NAME((PARSER)); \
} while (0)
#define H2_SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
do { \
const llh2_settings_t *settings; \
settings = (const llh2_settings_t *)(PARSER)->settings; \
if (settings == NULL || settings->NAME == NULL) \
{ \
err = 0; \
break; \
} \
err = settings->NAME((PARSER), (START), (LEN)); \
if (err == -1) \
{ \
err = H2PE_USER; \
llh2_set_error_reason((PARSER), "Span callback error in " #NAME); \
} \
} while (0)
int llh2__on_body(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
return err;
};
int llh2__on_frame_start(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_CALLBACK_MAYBE(s, on_frame_start);
return err;
};
int llh2__on_length(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_CALLBACK_MAYBE(s, on_length);
return err;
};
int llh2__on_type(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_CALLBACK_MAYBE(s, on_type);
return err;
};
int llh2__on_flags(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_CALLBACK_MAYBE(s, on_flags);
return err;
};
int llh2__on_body_start(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_CALLBACK_MAYBE(s, on_body_start);
return err;
};
int llh2__on_body_end(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_CALLBACK_MAYBE(s, on_body_end);
return err;
};
int llh2__on_frame_end(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_CALLBACK_MAYBE(s, on_frame_end);
return err;
};
int llh2__on_reset(
llh2_t *s, const unsigned char *p,
const unsigned char *endp){
int err;
H2_CALLBACK_MAYBE(s, on_reset);
return err;
};
void llh2_init(llh2_t *parser, const llh2_settings_t *settings)
{
llh2__internal_init(parser);
/* Unlike llhttp we reserve type for handling frames as
in the frame type according to RFC 9113 */
parser->settings = (void *)settings;
}
llh2_errno_t llh2_execute(llh2_t* parser, const char* data, size_t len){
return (llh2_errno_t)llh2__internal_execute(parser, data, data + len);
}
/* Private */
void llh2__debug(llh2_t *s, const char *p, const char *endp,
const char *msg)
{
if (p == endp) {
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
s->flags, msg);
} else {
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
s->type, s->flags, *p, msg);
}
}
#ifndef __LLH2_API_H__
#define __LLH2_API_H__
/* Temporarly */
#include "llh2.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__wasm__)
#define LLH2_EXPORT __attribute__((visibility("default")))
#elif defined(_WIN32)
#define LLH2_EXPORT __declspec(dllexport)
#else
#define LLH2_EXPORT
#endif
typedef llh2__internal_t llh2_t;
typedef struct llh2_settings_s llh2_settings_t;
typedef int (*llh2_data_cb)(llh2_t*, const char *at, size_t length);
typedef int (*llh2_cb)(llh2_t*);
struct llh2_settings_s {
llh2_data_cb on_body;
/* Called first thing when a frame is ready for parsing */
llh2_cb on_frame_start;
llh2_cb on_frame_end;
/* Called when length of body has been obtained */
llh2_cb on_length;
/* type obtained */
llh2_cb on_type;
/* flags obtained */
llh2_cb on_flags;
llh2_cb on_body_start;
llh2_cb on_body_end;
llh2_cb on_reset;
};
LLH2_EXPORT
void llh2_init(llh2_t* parser, const llh2_settings_t* settings);
LLH2_EXPORT
uint8_t llh2_get_type(llh2_t* parser);
LLH2_EXPORT
uint8_t llh2_get_flags(llh2_t* parser);
LLH2_EXPORT
uint32_t llh2_get_length(llh2_t* parser);
LLH2_EXPORT
uint32_t llh2_get_stream_id(llh2_t* parser);
LLH2_EXPORT
void llh2_resume(llh2_t* parser);
LLH2_EXPORT
llh2_errno_t llh2_execute(llh2_t* parser, const char* data, size_t len);
#ifdef __cplusplus
}
#endif
#endif // __LLH2_API_H__
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#ifdef __SSE4_2__
#ifdef _MSC_VER
#include <nmmintrin.h>
#else /* !_MSC_VER */
#include <x86intrin.h>
#endif /* _MSC_VER */
#endif /* __SSE4_2__ */
#ifdef __ARM_NEON__
#include <arm_neon.h>
#endif /* __ARM_NEON__ */
#ifdef __wasm__
#include <wasm_simd128.h>
#endif /* __wasm__ */
#ifdef _MSC_VER
#define ALIGN(n) _declspec(align(n))
#define UNREACHABLE __assume(0)
#else /* !_MSC_VER */
#define ALIGN(n) __attribute__((aligned(n)))
#define UNREACHABLE __builtin_unreachable()
#endif /* _MSC_VER */
#include "llh2.h"
typedef int (*llh2__internal__span_cb)(
llh2__internal_t*, const char*, const char*);
enum llparse_state_e {
s_error,
s_n_llh2__internal__n_pause,
s_n_llh2__internal__n_pause_1,
s_n_llh2__internal__n_pause_2,
s_n_llh2__internal__n_pause_3,
s_n_llh2__internal__n_pause_4,
s_n_llh2__internal__n_pause_5,
s_n_llh2__internal__n_pause_6,
s_n_llh2__internal__n_exit,
s_n_llh2__internal__n_pause_7,
s_n_llh2__internal__n_error_7,
s_n_llh2__internal__n_invoke_llh2__on_reset,
s_n_llh2__internal__n_error_6,
s_n_llh2__internal__n_invoke_llh2__on_frame_end,
s_n_llh2__internal__n_error_5,
s_n_llh2__internal__n_invoke_llh2__on_body_end,
s_n_llh2__internal__n_span_end_llh2__on_body,
s_n_llh2__internal__n_consume_length,
s_n_llh2__internal__n_span_start_llh2__on_body,
s_n_llh2__internal__n_error_4,
s_n_llh2__internal__n_invoke_llh2__on_body_start,
s_n_llh2__internal__n_stream_id_uint_32_le_byte4,
s_n_llh2__internal__n_stream_id_uint_32_le_byte3,
s_n_llh2__internal__n_stream_id_uint_32_le_byte2,
s_n_llh2__internal__n_stream_id_uint_32_le,
s_n_llh2__internal__n_error_3,
s_n_llh2__internal__n_invoke_llh2__on_flags,
s_n_llh2__internal__n_flags_uint_8,
s_n_llh2__internal__n_error_2,
s_n_llh2__internal__n_invoke_llh2__on_type,
s_n_llh2__internal__n_type_uint_8,
s_n_llh2__internal__n_error_1,
s_n_llh2__internal__n_invoke_llh2__on_length,
s_n_llh2__internal__n_length_uint_24_le_byte3,
s_n_llh2__internal__n_length_uint_24_le_byte2,
s_n_llh2__internal__n_length_uint_24_le,
s_n_llh2__internal__n_error,
s_n_llh2__internal__n_invoke_llh2__on_frame_start,
};
typedef enum llparse_state_e llparse_state_t;
int llh2__on_body (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__on_frame_start (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__on_length (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__on_type (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__on_flags (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__on_body_start (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__on_body_end (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__on_frame_end (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__on_reset (
llh2__internal_t* s, const unsigned char* p,
const unsigned char* endp);
int llh2__internal_init(llh2__internal_t* state) {
memset(state, 0, sizeof(*state));
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_invoke_llh2__on_frame_start;
return 0;
}
static llparse_state_t llh2__internal__run(
llh2__internal_t* state,
const unsigned char* p,
const unsigned char* endp) {
int match;
switch ((llparse_state_t) (intptr_t) state->_current) {
case s_n_llh2__internal__n_exit:
s_n_llh2__internal__n_exit : {
if (p == endp) {
return s_n_llh2__internal__n_exit;
}
p++;
goto s_n_llh2__internal__n_exit;
UNREACHABLE;
}
case s_n_llh2__internal__n_invoke_llh2__on_reset:
s_n_llh2__internal__n_invoke_llh2__on_reset : {
switch (llh2__on_reset(state, p, endp)) {
case 1:
goto s_n_llh2__internal__n_exit;
case 0:
goto s_n_llh2__internal__n_pause_7;
default:
goto s_n_llh2__internal__n_error_7;
}
UNREACHABLE;
}
case s_n_llh2__internal__n_invoke_llh2__on_frame_end:
s_n_llh2__internal__n_invoke_llh2__on_frame_end : {
switch (llh2__on_frame_end(state, p, endp)) {
case 1:
goto s_n_llh2__internal__n_pause_6;
case 0:
goto s_n_llh2__internal__n_invoke_llh2__on_reset;
default:
goto s_n_llh2__internal__n_error_6;
}
UNREACHABLE;
}
case s_n_llh2__internal__n_invoke_llh2__on_body_end:
s_n_llh2__internal__n_invoke_llh2__on_body_end : {
switch (llh2__on_body_end(state, p, endp)) {
case 1:
goto s_n_llh2__internal__n_pause_5;
case 0:
goto s_n_llh2__internal__n_invoke_llh2__on_frame_end;
default:
goto s_n_llh2__internal__n_error_5;
}
UNREACHABLE;
}
case s_n_llh2__internal__n_consume_length:
s_n_llh2__internal__n_consume_length : {
size_t avail;
size_t need;
avail = endp - p;
need = state->length;
if (avail >= need) {
p += need;
state->length = 0;
goto s_n_llh2__internal__n_span_end_llh2__on_body;
}
state->length -= avail;
return s_n_llh2__internal__n_consume_length;
UNREACHABLE;
}
case s_n_llh2__internal__n_span_start_llh2__on_body:
s_n_llh2__internal__n_span_start_llh2__on_body : {
if (p == endp) {
return s_n_llh2__internal__n_span_start_llh2__on_body;
}
state->_span_pos0 = (void*) p;
goto s_n_llh2__internal__n_consume_length;
UNREACHABLE;
}
case s_n_llh2__internal__n_stream_id_uint_32_le:
s_n_llh2__internal__n_stream_id_uint_32_le : {
if (p == endp) {
return s_n_llh2__internal__n_stream_id_uint_32_le;
}
state->stream_id = (*p);
p++;
goto s_n_llh2__internal__n_stream_id_uint_32_le_byte2;
UNREACHABLE;
}
case s_n_llh2__internal__n_flags_uint_8:
s_n_llh2__internal__n_flags_uint_8 : {
if (p == endp) {
return s_n_llh2__internal__n_flags_uint_8;
}
state->flags = (*p);
p++;
goto s_n_llh2__internal__n_invoke_llh2__on_flags;
UNREACHABLE;
}
case s_n_llh2__internal__n_type_uint_8:
s_n_llh2__internal__n_type_uint_8 : {
if (p == endp) {
return s_n_llh2__internal__n_type_uint_8;
}
state->type = (*p);
p++;
goto s_n_llh2__internal__n_invoke_llh2__on_type;
UNREACHABLE;
}
case s_n_llh2__internal__n_length_uint_24_le:
s_n_llh2__internal__n_length_uint_24_le : {
if (p == endp) {
return s_n_llh2__internal__n_length_uint_24_le;
}
state->length = (*p);
p++;
goto s_n_llh2__internal__n_length_uint_24_le_byte2;
UNREACHABLE;
}
case s_n_llh2__internal__n_invoke_llh2__on_frame_start:
s_n_llh2__internal__n_invoke_llh2__on_frame_start : {
switch (llh2__on_frame_start(state, p, endp)) {
case 1:
goto s_n_llh2__internal__n_pause;
case 0:
goto s_n_llh2__internal__n_length_uint_24_le;
default:
goto s_n_llh2__internal__n_error;
}
UNREACHABLE;
}
default:
UNREACHABLE;
}
s_n_llh2__internal__n_pause: {
state->error = 0x1;
state->reason = "on_frame_start pause";
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_length_uint_24_le;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_pause_1: {
state->error = 0x1;
state->reason = "on_length pause";
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_type_uint_8;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_pause_2: {
state->error = 0x1;
state->reason = "on_type pause";
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_flags_uint_8;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_pause_3: {
state->error = 0x1;
state->reason = "on_flags pause";
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_stream_id_uint_32_le;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_pause_4: {
state->error = 0x1;
state->reason = "on_body_start pause";
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_span_start_llh2__on_body;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_pause_5: {
state->error = 0x1;
state->reason = "on_body_end pause";
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_invoke_llh2__on_frame_end;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_pause_6: {
state->error = 0x1;
state->reason = "on_frame_end pause";
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_invoke_llh2__on_reset;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_pause_7: {
state->error = 0x1;
state->reason = "paused parser at on_reset";
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_invoke_llh2__on_frame_start;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_error_7: {
state->error = 0xc;
state->reason = "on_reset callback error";
state->error_pos = (const char*) p;
state->_current = (void*)(intptr_t) s_error;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_error_6: {
state->error = 0x5;
state->reason = "'on_frame_end' callback error";
state->error_pos = (const char*) p;
state->_current = (void*)(intptr_t) s_error;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_error_5: {
state->error = 0xb;
state->reason = "'on_body_end' callback error";
state->error_pos = (const char*) p;
state->_current = (void*)(intptr_t) s_error;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_span_end_llh2__on_body: {
const unsigned char* start;
int err;
start = state->_span_pos0;
state->_span_pos0 = NULL;
err = llh2__on_body(state, start, p);
if (err != 0) {
state->error = err;
state->error_pos = (const char*) p;
state->_current = (void*) (intptr_t) s_n_llh2__internal__n_invoke_llh2__on_body_end;
return s_error;
}
goto s_n_llh2__internal__n_invoke_llh2__on_body_end;
UNREACHABLE;
}
s_n_llh2__internal__n_error_4: {
state->error = 0xa;
state->reason = "'on_body_start' callback error";
state->error_pos = (const char*) p;
state->_current = (void*)(intptr_t) s_error;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_invoke_llh2__on_body_start: {
switch (llh2__on_body_start(state, p, endp)) {
case 1:
goto s_n_llh2__internal__n_pause_4;
case 0:
goto s_n_llh2__internal__n_span_start_llh2__on_body;
default:
goto s_n_llh2__internal__n_error_4;
}
UNREACHABLE;
}
s_n_llh2__internal__n_stream_id_uint_32_le_byte4: {
if (p == endp) {
return s_n_llh2__internal__n_stream_id_uint_32_le_byte4;
}
state->stream_id = (state->stream_id >> 8) | (*p);
p++;
goto s_n_llh2__internal__n_invoke_llh2__on_body_start;
UNREACHABLE;
}
s_n_llh2__internal__n_stream_id_uint_32_le_byte3: {
if (p == endp) {
return s_n_llh2__internal__n_stream_id_uint_32_le_byte3;
}
state->stream_id = (state->stream_id >> 8) | (*p);
p++;
goto s_n_llh2__internal__n_stream_id_uint_32_le_byte4;
UNREACHABLE;
}
s_n_llh2__internal__n_stream_id_uint_32_le_byte2: {
if (p == endp) {
return s_n_llh2__internal__n_stream_id_uint_32_le_byte2;
}
state->stream_id = (state->stream_id >> 8) | (*p);
p++;
goto s_n_llh2__internal__n_stream_id_uint_32_le_byte3;
UNREACHABLE;
}
s_n_llh2__internal__n_error_3: {
state->error = 0x9;
state->reason = "'on_flags' callback error";
state->error_pos = (const char*) p;
state->_current = (void*)(intptr_t) s_error;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_invoke_llh2__on_flags: {
switch (llh2__on_flags(state, p, endp)) {
case 1:
goto s_n_llh2__internal__n_pause_3;
case 0:
goto s_n_llh2__internal__n_stream_id_uint_32_le;
default:
goto s_n_llh2__internal__n_error_3;
}
UNREACHABLE;
}
s_n_llh2__internal__n_error_2: {
state->error = 0x8;
state->reason = "'on_type' callback error";
state->error_pos = (const char*) p;
state->_current = (void*)(intptr_t) s_error;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_invoke_llh2__on_type: {
switch (llh2__on_type(state, p, endp)) {
case 1:
goto s_n_llh2__internal__n_pause_2;
case 0:
goto s_n_llh2__internal__n_flags_uint_8;
default:
goto s_n_llh2__internal__n_error_2;
}
UNREACHABLE;
}
s_n_llh2__internal__n_error_1: {
state->error = 0x6;
state->reason = "'on_length' callback error";
state->error_pos = (const char*) p;
state->_current = (void*)(intptr_t) s_error;
return s_error;
UNREACHABLE;
}
s_n_llh2__internal__n_invoke_llh2__on_length: {
switch (llh2__on_length(state, p, endp)) {
case 1:
goto s_n_llh2__internal__n_pause_1;
case 0:
goto s_n_llh2__internal__n_type_uint_8;
default:
goto s_n_llh2__internal__n_error_1;
}
UNREACHABLE;
}
s_n_llh2__internal__n_length_uint_24_le_byte3: {
if (p == endp) {
return s_n_llh2__internal__n_length_uint_24_le_byte3;
}
state->length = (state->length >> 8) | (*p);
p++;
goto s_n_llh2__internal__n_invoke_llh2__on_length;
UNREACHABLE;
}
s_n_llh2__internal__n_length_uint_24_le_byte2: {
if (p == endp) {
return s_n_llh2__internal__n_length_uint_24_le_byte2;
}
state->length = (state->length >> 8) | (*p);
p++;
goto s_n_llh2__internal__n_length_uint_24_le_byte3;
UNREACHABLE;
}
s_n_llh2__internal__n_error: {
state->error = 0x4;
state->reason = "'on_frame_start' callback error";
state->error_pos = (const char*) p;
state->_current = (void*)(intptr_t) s_error;
return s_error;
UNREACHABLE;
}
}
int llh2__internal_execute(llh2__internal_t* state, const char* p, const char* endp) {
llparse_state_t next;
/* check lingering errors */
if (state->error != 0) {
return state->error;
}
/* restart spans */
if (state->_span_pos0 != NULL) {
state->_span_pos0 = (void*) p;
}
next = llh2__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp);
if (next == s_error) {
return state->error;
}
state->_current = (void*) (intptr_t) next;
/* execute spans */
if (state->_span_pos0 != NULL) {
int error;
error = llh2__on_body(state, state->_span_pos0, (const char*) endp);
if (error != 0) {
state->error = error;
state->error_pos = endp;
return error;
}
}
return 0;
}
#ifndef LLH2_C_HEADERS__H__
#define LLH2_C_HEADERS__H__
#ifdef __cplusplus
extern "C" {
#endif
enum llh2_reset {
H2RF_ERROR = -1,
H2RF_PAUSE = 0,
H2RF_EXIT = 1
};
typedef enum llh2_reset llh2_reset_t;
enum llh2_errno {
H2PE_OK = 0,
H2PE_PAUSED = 1,
H2PE_LENGTH_OVERFLOW = 2,
H2PE_STREAM_ID_OVERFLOW = 3,
H2PE_CB_ON_FRAME_START = 4,
H2PE_CB_ON_FRAME_END = 5,
H2PE_CB_ON_LENGTH = 6,
H2PE_CB_ON_STREAM_ID = 7,
H2PE_CB_ON_TYPE = 8,
H2PE_CB_ON_FLAG = 9,
H2PE_CB_ON_BODY_START = 10,
H2PE_CB_ON_BODY_END = 11,
H2PE_CB_ON_RESET = 12,
H2PE_EXIT = 13,
H2PE_USER = 14
};
typedef enum llh2_errno llh2_errno_t;
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LLH2_C_HEADERS__H__ */
#ifndef INCLUDE_LLH2_ITSELF_H_
#define INCLUDE_LLH2_ITSELF_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct llh2__internal_s llh2__internal_t;
struct llh2__internal_s {
int32_t _index;
void* _span_pos0;
int32_t error;
const char* reason;
const char* error_pos;
void* data;
void* _current;
void* settings;
uint32_t length;
uint32_t stream_id;
uint8_t type;
uint8_t flags;
};
int llh2__internal_init(llh2__internal_t* s);
int llh2__internal_execute(llh2__internal_t* s, const char* p, const char* endp);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* INCLUDE_LLH2_ITSELF_H_ */
from __future__ import annotations
from llparse import LLParse
from llparse.pybuilder.main_code import Node, _Match as CMatch
from enum import IntEnum, auto, IntFlag
from typing import TypedDict
# NOTE: There is a bug in llparse 0.1.4
# performing pip install git+https://github.com/Vizonex/pyllparse will fix this bug with pausing
class Encoding(IntEnum):
NONE = 0
HEX = 1
def buildEnum(name: str, prefix: str, e: IntEnum, encoding: Encoding = Encoding.NONE):
res = ""
_name = name.lower()
for k, v in e._member_map_.items():
if res:
res += ",\n"
res += f" {prefix}_{k} = "
if encoding == Encoding.HEX:
res += hex(v.value)
else:
res += f"{v.value}"
return (
f"enum {_name} " + "{\n" + res + "\n};\n" + f"typedef enum {_name} {_name}_t;\n"
)
def buildMap(name: str, d: dict[str, int]):
name = name.upper()
res = f"#define {name}_MAP(XX) \\\n"
for k, v in sorted(d.items()):
res += f" XX({v}, {k.replace('-', '')}, {k}) \\\n"
return res + "\n"
class UnpackCB(TypedDict):
length: CMatch
stream_id: CMatch
type: CMatch
flag: CMatch
class Error(IntEnum):
OK = 0
PAUSED = 1
# The only 2 that have a chance to overflow
LENGTH_OVERFLOW = auto()
STREAM_ID_OVERFLOW = auto()
# CALLBACKS
CB_ON_FRAME_START = auto()
CB_ON_FRAME_END = auto()
CB_ON_LENGTH = auto()
CB_ON_STREAM_ID = auto()
CB_ON_TYPE = auto()
CB_ON_FLAG = auto()
CB_ON_BODY_START = auto()
CB_ON_BODY_END = auto()
CB_ON_RESET = auto()
# User Chose to Exit from the loop after parsing a frame
EXIT = auto()
USER = auto()
class Reset(IntEnum):
ERROR = -1
PAUSE = 0
EXIT = 1
def buildCHeaders():
res = ""
res += "#ifndef LLH2_C_HEADERS__H__\n"
res += "#define LLH2_C_HEADERS__H__\n"
res += "#ifdef __cplusplus\n"
res += 'extern "C" {\n'
res += "#endif\n"
res += "\n"
res += buildEnum("llh2_reset", "H2RF", Reset) + "\n"
res += buildEnum("llh2_errno", "H2PE", Error) + "\n"
res += "#ifdef __cplusplus\n"
res += '} /* extern "C" */\n'
res += "#endif\n"
res += "#endif /* LLH2_C_HEADERS__H__ */\n"
return res
class FrameParser:
def __init__(self, llparse: LLParse):
self.llparse = llparse
p = llparse
self.u_cb = {
"on_frame_start": p.code.match("llh2__on_frame_start"),
"on_frame_end": p.code.match("llh2__on_frame_end"),
"on_length": p.code.match("llh2__on_length"),
# Part of stream_id I might look into fixing the reserved bit...
"on_stream_id": p.code.match("llh2__on_stream_id"),
"on_type": p.code.match("llh2__on_type"),
"on_flags": p.code.match("llh2__on_flags"),
# Frame Payload if any should exist or not...
"on_body_start": p.code.match("llh2__on_body_start"),
"on_body_end": p.code.match("llh2__on_body_end"),
# Frame Resetting...
# 0 -> Pause
# 1 -> Exit
# -1 -> Error
"on_reset": p.code.match("llh2__on_reset"),
}
self.on_reset = p.node("on_reset")
self.exit = p.node("exit")
self.on_body = p.span(p.code.span("llh2__on_body"))
def pause(self, msg: str, Next: Node | None = None):
res = self.llparse.pause(Error.PAUSED.value, msg)
if Next != None:
res.otherwise(Next)
return res
def invoke_pausable(self, name: str, Next: Node, error_code: Error):
p = self.llparse
return p.invoke(
self.u_cb[name],
{0: Next, int(Error.PAUSED): self.pause(f"{name} pause", Next)},
p.error(error_code.value, f"{name!r} callback error"),
)
def testFlag(
self,
flag: int | IntEnum,
Map: dict[IntEnum | IntFlag, Node],
Next: Node | None = None,
) -> Node:
p = self.llparse
res = p.invoke(
p.code.test("flags", int(flag)),
# Fix enums here because were all lazy
{k.value: v for k, v in Map.items()},
)
if Next is not None:
res.otherwise(Next)
return res
def build(self):
p = self.llparse
p.property("ptr", "settings")
p.property("i32", "length")
p.property("i32", "stream_id")
p.property("i8", "type")
p.property("i8", "flags")
length = p.uintLE("length", bits=3)
ty = p.uintLE("type", bits=1)
flags = p.uintLE("flags", bits=1)
stream_id = p.uintLE("stream_id", bits=4)
ty.skipTo(self.invoke_pausable("on_type", flags, Error.CB_ON_TYPE))
flags.skipTo(self.invoke_pausable("on_flags", stream_id, Error.CB_ON_FLAG))
length.skipTo(self.invoke_pausable("on_length", ty, Error.CB_ON_LENGTH))
start = self.invoke_pausable("on_frame_start", length, Error.CB_ON_FRAME_START)
end = self.invoke_pausable("on_frame_end", self.on_reset, Error.CB_ON_FRAME_END)
body_end = self.invoke_pausable("on_body_end", end, Error.CB_ON_BODY_END)
body_start = self.invoke_pausable(
"on_body_start",
self.on_body.start(
p.consume("length").otherwise(self.on_body.end(body_end))
),
Error.CB_ON_BODY_START,
)
# stream_id
stream_id.skipTo(body_start)
self.on_reset.otherwise(
p.invoke(
self.u_cb["on_reset"],
{
Reset.PAUSE.value: self.pause("paused parser at on_reset", start),
Reset.EXIT.value: self.exit,
},
).otherwise(p.error(Error.CB_ON_RESET, "on_reset callback error"))
)
self.exit.skipTo(self.exit)
return start
if __name__ == "__main__":
llh2 = LLParse("llh2__internal")
p = FrameParser(llh2)
src = llh2.build(
p.build(), header_name="llh2", headerGuard="INCLUDE_LLH2_ITSELF_H_"
)
with open("llh2.c", "w") as w:
w.write(src.c)
with open("llh2.h", "w") as w:
w.write(buildCHeaders())
w.write(src.header)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment