From 9d1eeab41e4d64557912d30a1478f1c43e91a96e Mon Sep 17 00:00:00 2001 From: wancy-x Date: Thu, 25 May 2023 14:40:24 +0800 Subject: [PATCH] update parse union by yyjson --- README.md | 57 ++++++- README_UNION.md | 125 ++++++++++++++ conf_with_union.json | 29 ++++ file.js | 4 + jstruct.js | 27 ++- union.js | 399 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 632 insertions(+), 9 deletions(-) create mode 100644 README_UNION.md create mode 100644 conf_with_union.json create mode 100644 union.js diff --git a/README.md b/README.md index 0df976c..5d969fb 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ An example configuration file is as follows: { "key": "arr", "type": "double", "req": true , "array": 4, "mlen": 2, "min": 1 }, { "key": "han", "type": "float", "req": false }, { "key": "st1", "type": "struct", "req": true, "name": "struct1" }, - { "key": "st2", "type": "struct", "req": true, "name": "struct2" } + { "key": "st2", "type": "struct", "req": true, "name": "struct2" }, + { "key": "od1", "type": "union", "req": true, "name": "order1" } ], "substruct": [ { @@ -49,6 +50,19 @@ An example configuration file is as follows: { "key": "str", "type": "char *", "req": true } ] } + ], + "subunion": [ + { + "name": "order1", + "protocol": "bfvp", + "key" : "word", + "type": "uint16_t", + "struct": "block", + "member": [ + { "key": "code1", "type": "uint32_t", "req": true, "bit": 7, "min": 0, "max": 16, "near": true }, + { "key": "code2", "type": "uint32_t", "req": false, "bit": 9 , "min": 0, "max": 32, "near": true } + ] + } ] } } @@ -92,6 +106,30 @@ struct: key: bar type: int req: true + subunion: + - + name: order1 + protocol: bfvp + key: word + type: uint16_t + struct: block + member: + - + key: code1 + type: uint32_t + req: true + bit: 7 + min: 0 + max: 16 + near: true + - + key: code2 + type: uint32_t + req: true + bit: 9 + min: 0 + max: 32 + near: false # ... ``` @@ -128,7 +166,14 @@ Struct member array. Each structure member is an object, which is used to descri ## struct.substruct + *{Array}* -Substruct array. Each substructure is an object. In the structure data nesting scenario, this field needs to be defined, same as `struct` rule. +Substruct array. Each substructure is an object. In the structure data nesting scenario, this field needs to be defined. + +**This field is optional**. + +## struct.subunion ++ *{Array}* + +Subunion array. Each subunion is an object. In the structure data nesting scenario, this field needs to be defined, same as `struct` rule. **This field is optional**. @@ -199,6 +244,14 @@ If the structure member type is `struct`, this field is required. This field is **This field is optional**. +# Substruct Member + +This structural rule is consistent with 'struct'. + +# Subunion Member + +Please refer to `'README_UNION.md'` for details. + # C Function The C functions generated by the jstruct tool are as follows. diff --git a/README_UNION.md b/README_UNION.md new file mode 100644 index 0000000..9b3212e --- /dev/null +++ b/README_UNION.md @@ -0,0 +1,125 @@ +# Subunion Member +Each structure member is called an `item`, this `item` can have the following description. + +Protocol types: + +Name|Full Name|Describe +---|---|--- +bfvp|Bit field value protocol|Utilizing 'union' to achieve rapid conversion of 'struct' and individual data +...|...|... + +# Bit field value protocol + +``` C +// bfvp: +union order1 +{ + uint16_t word; + struct + { +#if BYTE_ORDER == BIG_ENDIAN + uint32_t code1 :4; + uint32_t code2 :5; + uint32_t code3 :7; +#else + uint32_t code3 :7; + uint32_t code2 :5; + uint32_t code1 :4; +#endif + }block; +}; +``` + +## item.name ++ *{String}* + +The name of the C union. + +**This field is required**. + +## item.protocol ++ *{String}* + +The protocol of the C union., the accepted types include: +`'bfvp'`. + +**This field is required**. + +## item.key ++ *{String}* + +Value occupying the same space as the `'struct'`. + +**This field is required**. + +## item.type ++ *{String}* + +The type of the `'key'`, The type of the member of this structure, the value of this field must be equal to the sum of all subitems 'bit' in `'item.struct'`, the accepted types include: the accepted types include: +`'int8_t'`, `'int16_t'`, `'int32_t'`, `'uint8_t'`, `'uint16_t'`, `'uint32_t'`, `'int'`. + +**This field is required**. + +## item.struct ++ *{String}* + +The name of the C structure. + +**This field is required**. + +## item.member ++ *{Number}* + +Struct member array. Each structure member is an object, which is used to describe the name, type and other information of this member. + +**This field is required**. + +## item.member.key ++ *{Number}* + +The name of the member of this structure, the member name in the generated structure and the corresponding JSON object key. + +**This field is required**. + +## item.member.type ++ *{Number}* + +The type of the member of this structure, the accepted types include: +`'int8_t'`, `'int16_t'`, `'int32_t'`, `'uint8_t'`, `'uint16_t'`, `'uint32_t'`, `'int'`. + +**This field is required**. + +## item.member.req ++ *{Boolean}* + +Whether this struct member must exist when parsing JSON. When it is `false`, it means that if this member does not exist in JSON, it will be ignored during parsing. If it is `true`, if this member does not exist in JSON, the parsing function will return an error. **default: false**. + +**This field is optional**. + +## item.member.bit ++ *{Integer}* + +The bits of the member of this structure. + +**This field is required**. + +## item.member.min ++ *{Number}* + +If this member is a numeric type member, the minimum value allowed in JSON parsing. + +**This field is optional**. + +## item.member.max ++ *{Number}* + +If this member is a numeric type member, the maximum value allowed in JSON parsing. + +**This field is optional**. + +## item.member.near ++ *{Boolean}* + +If `item.min` or `item.max` exists, this field indicates the processing method when this member exceeds the limit when JSON is parsed. When it is `false`, it means that the parsing fails when the limit is exceeded, and when it is `true`, it means that it exceeds the limit that use limit value. **default: false**. + +**This field is optional**. \ No newline at end of file diff --git a/conf_with_union.json b/conf_with_union.json new file mode 100644 index 0000000..c23e051 --- /dev/null +++ b/conf_with_union.json @@ -0,0 +1,29 @@ +{ + "name": "test", + "struct": { + "name": "hello", + "member": [ + { "key": "foo", "type": "int", "req": true, "min": 1, "max": 65535, "near": true }, + { "key": "bar", "type": "int", "req": true, "max": 128 }, + { "key": "boo", "type": "bool", "req": false }, + { "key": "str", "type": "char *", "req": true }, + { "key": "arr", "type": "double", "req": true , "array": 4, "mlen": 2, "min": 1 }, + { "key": "han", "type": "float", "req": false }, + { "key": "od1", "type": "union", "req": true, "name": "order1" } + ], + "subunion": [ + { + "name": "order1", + "protocol": "bfvp", + "key" : "word", + "type": "uint16_t", + "struct": "block", + "member": [ + { "key": "code1", "type": "uint32_t", "req": true, "bit": 4, "min": 0, "max": 16, "near": true }, + { "key": "code2", "type": "uint32_t", "req": false, "bit": 5 , "min": 0, "max": 32, "near": true }, + { "key": "code3", "type": "uint32_t", "req": false, "bit": 7 , "min": 0, "max": 160, "near": true } + ] + } + ] + } + } \ No newline at end of file diff --git a/file.js b/file.js index d674ec1..c7606d2 100644 --- a/file.js +++ b/file.js @@ -75,6 +75,10 @@ exports.H_HEADER = #include #include #include + +#ifdef __linux__ +#include +#endif `; /* diff --git a/jstruct.js b/jstruct.js index 6fe9cc5..5e28019 100644 --- a/jstruct.js +++ b/jstruct.js @@ -8,7 +8,7 @@ * * Author: Han.hui * - * Version: 1.0.3 + * Version: 1.0.4 * */ @@ -17,7 +17,7 @@ var process = require('process'); /* Supported types include */ const STRUCT_TYPES = [ - 'bool', 'int8_t', 'int16_t', 'int32_t', 'uint8_t', 'uint16_t', 'uint32_t', 'int', 'long', 'float', 'double', 'char *', 'struct' + 'bool', 'int8_t', 'int16_t', 'int32_t', 'uint8_t', 'uint16_t', 'uint32_t', 'int', 'long', 'float', 'double', 'char *', 'struct', 'union' ]; /* Struct arry */ @@ -77,6 +77,11 @@ const FILE = require('./file'); */ const FUNC = require('./func'); +/* + * Union Parse + */ +const UNION = require('./union'); + /* * Check type */ @@ -120,13 +125,13 @@ function gen_struct_def(struct, structlist, issub) { } interval = item.type.endsWith('*') ? '' : ' '; if (item.array) { - if (item.type === 'struct') { + if (item.type === 'struct' || item.type === 'union') { body += `\t${item.type}${interval}${item.name}${interval}${item.key}[${item.array}];\n`; } else { body += `\t${item.type}${interval}${item.key}[${item.array}];\n`; } } else { - if (item.type === 'struct') { + if (item.type === 'struct' || item.type === 'union') { body += `\t${item.type}${interval}${item.name}${interval}${item.key};\n`; } else { body += `\t${item.type}${interval}${item.key};\n`; @@ -149,7 +154,7 @@ function gen_struct() { return ` #ifndef STRUCT_${CONF.struct.name.toUpperCase()}_DEFINED #define STRUCT_${CONF.struct.name.toUpperCase()}_DEFINED -${gen_struct_def(CONF.struct, CONF.struct.substruct ? CONF.struct.substruct:'', false)} +${UNION.gen_union_def(CONF.struct.subunion ? CONF.struct.subunion:'')}${gen_struct_def(CONF.struct, CONF.struct.substruct ? CONF.struct.substruct:'', false)} #endif /* STRUCT_${CONF.struct.name.toUpperCase()}_DEFINED */ `; } @@ -157,8 +162,9 @@ ${gen_struct_def(CONF.struct, CONF.struct.substruct ? CONF.struct.substruct:'', /* * Generate function define */ -function gen_func_def(structlist) { +function gen_func_def(structlist, unionlist) { var body = ''; + body += UNION.gen_union_func_def(unionlist); for (var itemst of structlist) { if (struct_names.indexOf(itemst.name) >= 0) { continue; @@ -175,7 +181,7 @@ static yyjson_mut_val *${itemst.name}_json_object_stringify(yyjson_mut_doc *, co * Generate function */ function gen_sub_func(FUNC) { - var defs = gen_func_def(CONF.struct.substruct ? CONF.struct.substruct:''); + var defs = gen_func_def(CONF.struct.substruct ? CONF.struct.substruct:'', CONF.struct.subunion ? CONF.struct.subunion:''); return defs ? `${FUNC.FUNCTION_OBJECT_DECLARATION}\n${defs}\n` : ''; } @@ -356,6 +362,7 @@ function gen_json_parse(FUNC) { deserial += gen_deserial(item, 'str'); break; case 'struct': + case 'union': deserial += gen_deserial(item, 'obj'); break; } @@ -422,6 +429,7 @@ function gen_json_object_parse(struct) { deserial += gen_deserial(item, 'str'); break; case 'struct': + case 'union': deserial += gen_deserial(item, 'obj'); break; } @@ -571,6 +579,7 @@ function gen_json_stringify(FUNC) { serial += gen_serial(item, 'str'); break; case 'struct': + case 'union': serial += gen_serial(item, 'val'); break; } @@ -648,6 +657,7 @@ function gen_json_object_stringify(struct) { serial += gen_serial(item, 'str'); break; case 'struct': + case 'union': serial += gen_serial(item, 'val'); break; } @@ -708,6 +718,9 @@ function c_merge(FILE, FUNC) { body += gen_parse_free(FUNC); body += gen_json_stringify(FUNC); body += gen_stringify_free(FUNC); + if (CONF.struct.hasOwnProperty("subunion")) { + body += UNION.gen_union_json_object(CONF.struct.subunion); + } if (CONF.struct.hasOwnProperty("substruct")) { for (var itemst of CONF.struct.substruct) { if (struct_names.indexOf(itemst.name) >= 0) { diff --git a/union.js b/union.js new file mode 100644 index 0000000..2a65412 --- /dev/null +++ b/union.js @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2021 ACOAUTO Team. + * All rights reserved. + * + * Detailed license information can be found in the LICENSE file. + * + * File: protocol.js Union <-> C struct tool use cJSON library. + * + * Author: Wan.ChunYu + * + * Version: 1.0.3 + * + */ + +/* + * Protocol types: + * +-------+--------------------------+-------------------------------------------------------------------------------+ + * | Name | Full Name | Describe | + * +-------+----------------------------------------------------------------------------------------------------------+ + * | bfvp | Bit field value protocol | Utilizing 'union' to achieve rapid conversion of 'struct' and individual data | + * +-------+--------------------------+-------------------------------------------------------------------------------+ + * + * bfvp: + * union order1 + * { + * uint16_t word; + * struct + * { + * #if BYTE_ORDER == BIG_ENDIAN + * uint32_t code1 :4; + * uint32_t code2 :5; + * uint32_t code3 :7; + * #else + * uint32_t code3 :7; + * uint32_t code2 :5; + * uint32_t code1 :4; + * #endif + * }block; + * }; + * + */ + +/* Int Number types include */ +const INT_NUMBER_TYPES = [ + 'int8_t', 'int16_t', 'int32_t', 'uint8_t', 'uint16_t', 'uint32_t', 'int' +]; + +/* Union arry */ +var union_names = []; + +/* + * Check int number type + */ +function check_int_number_type(type) { + if (!INT_NUMBER_TYPES.includes(type)) { + throw new TypeError('Union type error, legal includes:', JSON.stringify(INT_NUMBER_TYPES)); + } +} + +/* + * Check number type bits + */ +function check_bit_sum(type, bit) { + var number = 0; + switch (type) { + case 'int8_t': + case 'uint8_t': + number = 8; + break; + case 'int16_t': + case 'uint16_t': + number = 16; + break; + case 'int32_t': + case 'uint32_t': + case 'int': + number = 32; + break; + default: + break; + } + if (number !== bit) { + throw new TypeError(`${type} type is ${number} bits, but your total is ${bit} bits!`); + } +} + +/* + * Generate error collect + */ +function gen_error(issub) { + var jdel = issub ? '' : 'yyjson_doc_free(doc);\n\t'; + var error = `\n +error: + ${jdel}return (false);`; + return error; +} + +/* + * Generate range assign + */ +function gen_range_assign(item, variable, indentation) { + var assign = ''; + var handle = undefined; + if (typeof item.min === 'number') { + if (item.near) { + handle = `${variable} = ${item.min};`; + } else { + handle = 'goto error;'; + } + assign = +`if (${variable} < ${item.min}) { +${indentation}\t${handle} +${indentation}}`; + } + if (typeof item.max === 'number') { + if (item.near) { + handle = `${variable} = ${item.max};`; + } else { + handle = 'goto error;'; + } + if (assign) { + assign += +` else if (${variable} > ${item.max}) { +${indentation}\t${handle} +${indentation}}`; + } else { + assign = +`if (${variable} > ${item.max}) { +${indentation}\t${handle} +${indentation}}`; + } + } + return assign; +} + +/* Number functype */ +const numfuncs = new Set(['int', 'uint', 'real']); + +/* + * Generate assign + */ +function gen_assign(item, target, functype, indentation) { + if (numfuncs.has(functype) && + (typeof item.min === 'number' || typeof item.max === 'number')) { + var assign = `v = yyjson_get_${functype}(item); +${indentation}${gen_range_assign(item, 'v', indentation)} +${indentation}${target} = (${item.type})v;`; + } else if (functype === 'obj') { + if (!item.name) { + throw new Error('Structure "struct" type member must have "name" field!'); + } + var assign = `if (!${item.name}_json_object_parse(item, &(${target}))) { +${indentation} goto error; +${indentation}}`; + } else { + var assign = `${target} = (${item.type})yyjson_get_${functype}(item);`; + } + return assign; +} + +/* + * Generate union 'bfvp' define + */ +function gen_union_bfvp_def(itemun) { + var body = ''; + var big_endian_body = ''; + var little_endian_body = ''; + var interval = ' '; + var member = itemun.member; + var bit = 0; + if (!itemun.type || !itemun.key || !itemun.struct) { + throw new Error('Protocol "bfvp" must include "type"、"key" and "struct" field!'); + } + for (var item of member) { + if (item.key === 'json') { + throw new Error('Union members are not allowed to be named "json"!'); + } + if (item.array) { + throw new Error('Union members are not allowed "array" field!'); + } + if (!item.bit) { + throw new Error('Union members must include "bit" field!'); + } + check_int_number_type(item.type); + + big_endian_body += `\t\t${item.type}${interval}${item.key}\t:${item.bit};\n`; + little_endian_body = `\t\t${item.type}${interval}${item.key}\t:${item.bit};\n` + little_endian_body; + bit += item.bit; + } + check_bit_sum(itemun.type, bit); + body = `#if BYTE_ORDER == BIG_ENDIAN\n${big_endian_body}#else\n${little_endian_body}#endif\n` + return `\nunion${interval}${itemun.name}\n{\n\t${itemun.type}${interval}${itemun.key};\n\tstruct\n\t{\n${body}\t}${itemun.struct};\n};\n`; +} + +/* + * Generate json_stringify() + */ +function gen_bfvp_json_object_stringify(union) { + var body = ''; + var member = union.member; + var serial = ''; + for (var item of member) { + switch (item.type) { + case 'int': + case 'long': + case 'int8_t': + case 'int16_t': + case 'int32_t': + serial += gen_bfvp_serial(item, union.struct, 'int'); + break; + case 'uint8_t': + case 'uint16_t': + case 'uint32_t': + serial += gen_bfvp_serial(item, union.struct, 'uint'); + break; + } + } + + body += ` +/* + * Serialize the union '${union.name}' into a JSON string + */ +static yyjson_mut_val *${union.name}_json_object_stringify (yyjson_mut_doc *doc, const union ${union.name} *des) +{ + size_t i; + yyjson_mut_val *val, *item, *array; + + (void)i; + (void)item; + (void)array; + + if (!des) { + return (NULL); + } + + val = yyjson_mut_obj(doc); + if (!val) { + return (NULL); + } +${serial} + return (val); + +error: + return (NULL); +}\n`; + return body; +} + +/* + * Generate json_object_parse() + */ +function gen_bfvp_json_object_parse(union) { + var body = ''; + var member = union.member; + var deserial = ''; + for (var item of member) { + switch (item.type) { + case 'int': + case 'long': + case 'int8_t': + case 'int16_t': + case 'int32_t': + deserial += gen_bfvp_deserial(item, union.struct, 'int'); + break; + case 'uint8_t': + case 'uint16_t': + case 'uint32_t': + deserial += gen_bfvp_deserial(item, union.struct, 'uint'); + break; + } + } + var error = deserial.includes('goto\t') ? gen_error(true) : ''; + + body += ` +/* + * Deserialize the JSON Object into a union '${union.name}' + */ +static bool ${union.name}_json_object_parse (yyjson_val *val, union ${union.name} *des) +{ + size_t i, max, sza; + register double v; + yyjson_val *item, *array; + + (void)i; + (void)v; + (void)max; + (void)sza; + (void)item; + (void)array; + + if (!val || !des) { + return (false); + } +${deserial} + return (true);${error} +}\n`; + return body; +} + +/* + * Generate 'bfvp' serial + */ +function gen_bfvp_serial(item, struct, functype) { + var value = `des->${struct}.${item.key}`; + var serial = ` + if (!yyjson_mut_obj_add_${functype}(doc, val, "${item.key}", ${value})) { + goto error; + }\n`; + return serial; +} + +/* + * Generate 'bfvp' deserial + */ +function gen_bfvp_deserial(item, struct, functype) { + if (item.req) { + var failed = ` else { + goto error; + }`; + } else { + var failed = ''; + } + var deserial = ` + item = yyjson_obj_get(val, "${item.key}"); + if (item && yyjson_is_${functype}(item)) { + ${gen_assign(item, `des->${struct}.${item.key}`, functype, '\t\t')} + }${failed}\n`; + return deserial; +} + +/* + * Generate union define + */ +function gen_union_def(unionlist) { + var body = ''; + if (unionlist) { + for (var itemun of unionlist) { + if (union_names.indexOf(itemun.name) >= 0) { + return ''; + } + switch (itemun.protocol) { + case 'bfvp': + body += gen_union_bfvp_def(itemun); + break; + default: + console.info(`Protocol '${itemun.protocol}' is not support!`); + break; + } + union_names.push(itemun.name); + } + } + union_names = []; + return `${body}`; +} + +/* + * Generate function define + */ +function gen_union_func_def(unionlist) { + var body = ''; + for (var itemun of unionlist) { + if (union_names.indexOf(itemun.name) >= 0) { + continue; + } + body += +`static bool ${itemun.name}_json_object_parse(yyjson_val *, union ${itemun.name} *); +static yyjson_mut_val *${itemun.name}_json_object_stringify(yyjson_mut_doc *, const union ${itemun.name} *);\n`; + union_names.push(itemun.name); + } + union_names = []; + return `${body}`; +} + +function gen_union_json_object(subunion) { + var body = ''; + for (var itemun of subunion) { + if (union_names.indexOf(itemun.name) >= 0) { + continue; + } + switch (itemun.protocol) { + case 'bfvp': + body += gen_bfvp_json_object_parse(itemun); + body += gen_bfvp_json_object_stringify(itemun); + break; + default: + console.info(`Protocol '${itemun.protocol}' is not support!`); + break; + } + union_names.push(itemun.name); + } + union_names = []; + return body; +} + +module.exports = { + gen_union_json_object:gen_union_json_object, + gen_union_def:gen_union_def, + gen_union_func_def:gen_union_func_def +} \ No newline at end of file