obex_parser: add obex_app_param_parser

This commit is contained in:
Matthias Ringwald 2022-04-11 22:34:22 +02:00
parent 63cff0c949
commit 3a6b6ac96a
3 changed files with 233 additions and 3 deletions

View File

@ -249,3 +249,89 @@ obex_parser_header_state_t obex_parser_header_store(uint8_t * header_buffer, uin
return OBEX_PARSER_HEADER_INCOMPLETE;
}
}
/* OBEX App Param Parser */
void obex_app_param_parser_init(obex_app_param_parser_t * parser, obex_app_param_parser_callback_t callback, uint8_t param_size, void * user_data){
parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_TYPE;
parser->callback = callback;
parser->user_data = user_data;
parser->param_size = param_size;
parser->param_pos = 0;
}
obex_app_param_parser_params_state_t obex_app_param_parser_process_data(obex_app_param_parser_t *parser, const uint8_t *data_buffer, uint16_t data_len){
while ((data_len > 0) && (parser->param_pos < parser->param_size)){
uint16_t bytes_to_consume = 1;
switch(parser->state){
case OBEX_APP_PARAM_PARSER_STATE_INVALID:
return OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID;
case OBEX_APP_PARAM_PARSER_STATE_W4_TYPE:
parser->tag_id = *data_buffer;
parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_LEN;
break;
case OBEX_APP_PARAM_PARSER_STATE_W4_LEN:
parser->tag_len = *data_buffer;
if ((parser->param_pos + parser->tag_len) > parser->param_size){
parser->state = OBEX_APP_PARAM_PARSER_STATE_INVALID;
return OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID;
}
parser->tag_pos = 0;
parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_VALUE;
break;
case OBEX_APP_PARAM_PARSER_STATE_W4_VALUE:
bytes_to_consume = btstack_min(parser->tag_len - parser->tag_pos, data_len);
(*parser->callback)(parser->user_data, parser->tag_id, parser->tag_len, parser->tag_pos, data_buffer, bytes_to_consume);
parser->tag_pos += bytes_to_consume;
if (parser->tag_pos == parser->tag_len){
parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_TYPE;
}
break;
default:
btstack_unreachable();
break;
}
data_buffer += bytes_to_consume;
data_len -= bytes_to_consume;
parser->param_pos += bytes_to_consume;
// all bytes read? then check state
if (parser->param_pos == parser->param_size){
if (parser->state == OBEX_APP_PARAM_PARSER_STATE_W4_TYPE){
parser->state = OBEX_APP_PARAM_PARSER_STATE_COMPLETE;
} else {
parser->state = OBEX_APP_PARAM_PARSER_STATE_INVALID;
}
}
}
if (data_len > 0){
return OBEX_APP_PARAM_PARSER_PARAMS_STATE_OVERRUN;
}
switch (parser->state){
case OBEX_APP_PARAM_PARSER_STATE_COMPLETE:
return OBEX_APP_PARAM_PARSER_PARAMS_STATE_COMPLETE;
case OBEX_APP_PARAM_PARSER_STATE_INVALID:
return OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID;
default:
return OBEX_APP_PARAM_PARSER_PARAMS_STATE_INCOMPLETE;
}
}
obex_app_param_parser_tag_state_t obex_app_param_parser_tag_store(uint8_t * header_buffer, uint8_t buffer_size, uint8_t total_len,
uint8_t data_offset, const uint8_t * data_buffer, uint8_t data_len){
uint16_t bytes_to_store = btstack_min(buffer_size - data_offset, data_len);
memcpy(&header_buffer[data_offset], data_buffer, bytes_to_store);
uint16_t new_offset = data_offset + bytes_to_store;
if (new_offset > buffer_size){
return OBEX_APP_PARAM_PARSER_TAG_OVERRUN;
} else if (new_offset == total_len) {
return OBEX_APP_PARAM_PARSER_TAG_COMPLETE;
} else {
return OBEX_APP_PARAM_PARSER_TAG_INCOMPLETE;
}
}

View File

@ -113,10 +113,54 @@ typedef struct {
uint8_t header_id;
} obex_parser_t;
/**
* Callback to process chunked data
* @param user_data provided in obex_parser_init
* @param header_id current OBEX header ID
* @param total_len of header
* @param data_offset
* @param data_len
* @param data_buffer
*/
typedef void (*obex_app_param_parser_callback_t)(void * user_data, uint8_t tag_id, uint8_t total_len, uint8_t data_offset, const uint8_t * data_buffer, uint8_t data_len);
typedef enum {
OBEX_APP_PARAM_PARSER_PARAMS_STATE_INCOMPLETE,
OBEX_APP_PARAM_PARSER_PARAMS_STATE_COMPLETE,
OBEX_APP_PARAM_PARSER_PARAMS_STATE_OVERRUN,
OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID,
} obex_app_param_parser_params_state_t;
typedef enum {
OBEX_APP_PARAM_PARSER_TAG_INCOMPLETE,
OBEX_APP_PARAM_PARSER_TAG_COMPLETE,
OBEX_APP_PARAM_PARSER_TAG_OVERRUN,
} obex_app_param_parser_tag_state_t;
typedef enum {
OBEX_APP_PARAM_PARSER_STATE_W4_TYPE = 0,
OBEX_APP_PARAM_PARSER_STATE_W4_LEN,
OBEX_APP_PARAM_PARSER_STATE_W4_VALUE,
OBEX_APP_PARAM_PARSER_STATE_COMPLETE,
OBEX_APP_PARAM_PARSER_STATE_INVALID,
OBEX_APP_PARAM_PARSER_STATE_OVERRUN,
} obex_app_param_parser_state_t;
typedef struct {
obex_app_param_parser_callback_t callback;
obex_app_param_parser_state_t state;
void * user_data;
uint16_t param_size;
uint16_t param_pos;
uint16_t tag_len;
uint16_t tag_pos;
uint8_t tag_id;
} obex_app_param_parser_t;
/**
* Initialize OBEX Parser for next OBEX request
* @param obex_parser
* @param function to call for fields that are not registered
* @param function to call for field data
* @param user_data provided to callback function
*/
void obex_parser_init_for_request(obex_parser_t * obex_parser, obex_parser_callback_t obex_parser_callback, void * user_data);
@ -125,7 +169,7 @@ void obex_parser_init_for_request(obex_parser_t * obex_parser, obex_parser_callb
* Initialize OBEX Parser for next OBEX response
* @param obex_parser
* @param opcode of request - needed as responses with additional fields like connect and set path
* @param function to call for fields that are not registered
* @param function to call for field data
* @param user_data provided to callback function
*/
void obex_parser_init_for_response(obex_parser_t * obex_parser, uint8_t opcode, obex_parser_callback_t obex_parser_callback, void * user_data);
@ -151,7 +195,7 @@ void obex_parser_get_operation_info(obex_parser_t * obex_parser, obex_parser_ope
* @param header_buffer
* @param buffer_size of header_buffer
* @param total_len of header value
* @param data_offset of chunkc to store
* @param data_offset of chunk to store
* @param data_buffer
* @param data_len chunk length
* @return OBEX_PARSER_HEADER_COMPLETE when header value complete
@ -159,6 +203,38 @@ void obex_parser_get_operation_info(obex_parser_t * obex_parser, obex_parser_ope
obex_parser_header_state_t obex_parser_header_store(uint8_t * header_buffer, uint16_t buffer_size, uint16_t total_len,
uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len);
/**
* Initialize OBEX Application Param Parser
* @param parser
* @param function to call for tag data
* @param param_size of OBEX_HEADER_APPLICATION_PARAMETERS header
* @param user_data provided to callback function
*/
void obex_app_param_parser_init(obex_app_param_parser_t * parser, obex_app_param_parser_callback_t callback, uint8_t param_size, void * user_data);
/**
* Process OBEX App Param data
* @param parser
* @param data_len
* @param data_buffer
* @return OBEX_APP_PARAM_PARSER_PARAMS_STATE_COMPLETE if packet has been completely parsed
*/
obex_app_param_parser_params_state_t obex_app_param_parser_process_data(obex_app_param_parser_t *parser, const uint8_t *data_buffer, uint16_t data_len);
/**
* Helper to collect tag chunks in fixed-size data buffer
* @param tag_buffer
* @param buffer_size of data buffer
* @param total_len of tag value
* @param data_offset of chunk to store
* @param data_buffer
* @param data_len chunk length
* @return OBEX_APP_PARAM_PARSER_TAG_COMPLETE when tag complete
*/
obex_app_param_parser_tag_state_t obex_app_param_parser_tag_store(uint8_t * tag_buffer, uint8_t buffer_size, uint8_t total_len,
uint8_t data_offset, const uint8_t * data_buffer, uint8_t data_len);
/* API_END */

View File

@ -137,6 +137,74 @@ TEST(OBEX_PARSER, SetPathResponse){
parse_response(OBEX_OPCODE_SETPATH);
}
/** App Param Parser */
static uint8_t test_tag_id;
static uint8_t test_tag_buffer[100];
static uint16_t test_tag_len;
void app_param_parser_callback(void * user_data, uint8_t tag_id, uint8_t total_len, uint8_t data_offset, const uint8_t * data_buffer, uint8_t data_len){
if (obex_app_param_parser_tag_store(test_header_buffer, sizeof(test_header_buffer), total_len, data_offset, data_buffer, data_len) == OBEX_APP_PARAM_PARSER_TAG_COMPLETE){
test_tag_len = total_len;
test_tag_id = tag_id;
}
}
TEST_GROUP(APP_PARAM_PARSER){
obex_app_param_parser_t parser;
void setup(void){
test_tag_id = 0;
test_tag_len = 0;
}
void teardown(void){
}
void parse_app_params(const uint8_t * app_params, uint8_t param_len){
obex_app_param_parser_init(&parser, &app_param_parser_callback, param_len, NULL);
for (int i = 0; i < param_len - 1;i++){
obex_app_param_parser_params_state_t parser_state = obex_app_param_parser_process_data(&parser, &app_params[i], 1);
CHECK_EQUAL(OBEX_APP_PARAM_PARSER_PARAMS_STATE_INCOMPLETE, parser_state);
}
if (param_len > 0){
obex_app_param_parser_params_state_t parser_state = obex_app_param_parser_process_data(&parser, &app_params[param_len-1], 1);
CHECK_EQUAL(OBEX_APP_PARAM_PARSER_PARAMS_STATE_COMPLETE, parser_state);
}
}
};
TEST(APP_PARAM_PARSER, EmptyParams){
parse_app_params(NULL, 0);
CHECK_EQUAL(0, test_tag_id);
CHECK_EQUAL(0, test_tag_len);
}
TEST(APP_PARAM_PARSER, SingleParam){
uint8_t message[] = { 0x01, 0x02, 0x03, 0x4};
parse_app_params(message, sizeof(message));
CHECK_EQUAL(1, test_tag_id);
CHECK_EQUAL(2, test_tag_len);
}
TEST(APP_PARAM_PARSER, Overrun){
uint8_t message[] = { 0x01, 0x02, 0x03, 0x4};
parse_app_params(message, sizeof(message));
obex_app_param_parser_params_state_t parser_state = obex_app_param_parser_process_data(&parser, &message[0], 1);
CHECK_EQUAL(OBEX_APP_PARAM_PARSER_PARAMS_STATE_OVERRUN, parser_state);
CHECK_EQUAL(1, test_tag_id);
CHECK_EQUAL(2, test_tag_len);
}
TEST(APP_PARAM_PARSER, InvalidTagLen){
uint8_t message[] = { 0x01, 0x04, 0x03, 0x4};
obex_app_param_parser_t parser;
obex_app_param_parser_init(&parser, &app_param_parser_callback, sizeof(message), NULL);
obex_app_param_parser_params_state_t parser_state;
parser_state = obex_app_param_parser_process_data(&parser, &message[0], 1);
CHECK_EQUAL(OBEX_APP_PARAM_PARSER_PARAMS_STATE_INCOMPLETE, parser_state);
parser_state = obex_app_param_parser_process_data(&parser, &message[1], 1);
CHECK_EQUAL(OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID, parser_state);
CHECK_EQUAL(0, test_tag_id);
CHECK_EQUAL(0, test_tag_len);
}
int main (int argc, const char * argv[]){
return CommandLineTestRunner::RunAllTests(argc, argv);
}