Created
September 17, 2025 22:18
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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); | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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__ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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_ */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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