1 /* protobuf_lang_tree.c
3 * Routines of building and reading Protocol Buffers Language grammar tree.
4 * Copyright 2019, Huang Qiangxiong <qiangxiong.huang@qq.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include "protobuf_lang_tree.h"
19 #include "protobuf-helper.h" /* only for PROTOBUF_TYPE_XXX enumeration */
21 #define MAX_PROTOBUF_NODE_DEPTH 100
23 check_node_depth(const pbl_node_t
*node
)
26 for (const pbl_node_t
*parent
= node
; parent
; parent
= parent
->parent
) {
29 if (depth
> MAX_PROTOBUF_NODE_DEPTH
) {
36 pbl_parser_error(protobuf_lang_state_t
*state
, const char *fmt
, ...);
38 /* Unescape string to bytes according to:
40 * strLit = ( { charValue } ) | ( "'" { charValue } "'" ) | ( '"' { charValue } '"' )
41 * charValue = hexEscape | octEscape | charEscape | /[^\0\n\\]/
42 * hexEscape = '\' ( "x" | "X" ) hexDigit hexDigit
43 * octEscape = '\' octalDigit octalDigit octalDigit
44 * charEscape = '\' ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | '\' | "'" | '"' )
46 * @param src the source escaped string terminated by '\0'
47 * @param [out] size the length of the output byte array.
48 * @return the unescaped byte array, should be released by g_free()
51 protobuf_string_unescape(const char* src
, int* size
)
57 if (!(src
&& size
&& (src_len
= (int)strlen(src
))))
60 dst
= q
= (uint8_t *) g_malloc0(src_len
+ 1);
62 while (p
< src
+ src_len
&& *p
) {
66 if (*p
== 'x' || *p
== 'X') { /* unescape hex byte */
67 *q
++ = (uint8_t)strtol(p
+ 1, (char**)&p
, 16);
71 if (*p
>= '0' && *p
<= '7') { /* unescape octal byte */
72 *q
++ = (uint8_t)strtol(p
, (char**)&p
, 8);
78 case 'a': *q
++ = '\a'; break;
79 case 'b': *q
++ = '\b'; break;
80 case 'f': *q
++ = '\f'; break;
81 case 'n': *q
++ = '\n'; break;
82 case 'r': *q
++ = '\r'; break;
83 case 't': *q
++ = '\t'; break;
84 case 'v': *q
++ = '\v'; break;
85 default: /* include copying '\', "'" or '"' */
95 *size
= (int)(q
- dst
);
101 Reinitialize the protocol buffers pool according to proto files directories.
102 @param ppool The output descriptor_pool will be created. If *pool is not NULL, it will free it first.
103 @param directories The root directories containing proto files. Must end with NULL element.
104 @param error_cb The error reporter callback function.
107 pbl_reinit_descriptor_pool(pbl_descriptor_pool_t
** ppool
, const char** directories
, pbl_report_error_cb_t error_cb
)
111 pbl_free_pool(*ppool
);
112 pbl_descriptor_pool_t
* p
= g_new0(pbl_descriptor_pool_t
, 1);
114 p
->source_paths
= g_queue_new();
115 for (i
= 0; directories
[i
] != NULL
; i
++) {
116 g_queue_push_tail(p
->source_paths
, g_strdup(directories
[i
]));
119 p
->error_cb
= error_cb
? error_cb
: pbl_printf
;
120 p
->packages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, pbl_free_node
);
121 p
->proto_files
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
122 p
->proto_files_to_be_parsed
= g_queue_new();
127 /* free all memory used by this protocol buffers language pool */
129 pbl_free_pool(pbl_descriptor_pool_t
* pool
)
131 if (pool
== NULL
) return;
133 g_queue_free_full(pool
->source_paths
, g_free
);
134 g_hash_table_destroy(pool
->packages
);
135 g_queue_free(pool
->proto_files_to_be_parsed
); /* elements will be removed in p->proto_files */
136 g_hash_table_destroy(pool
->proto_files
);
141 /* Canonicalize absolute file path. We only accept path like:
142 * /home/test/protos/example.proto
143 * D:/mydir/test/example.proto
144 * d:\mydir\test\example.proto
145 * This function will replace all '\' to '/', and change '//' to '/'.
146 * May use _fullpath() in windows or realpath() in *NIX.
147 * Return a newly-allocated path, NULL if failed (file
148 * does not exist, or file path contains '/../').
151 pbl_canonicalize_absolute_filepath(const char* path
)
154 char* canon_path
= g_new(char, strlen(path
) + 1);
155 /* replace all '\' to '/', and change '//' to '/' */
156 for (i
= 0, j
= 0; path
[i
] != '\0'; i
++) {
157 if (path
[i
] == '\\' || path
[i
] == '/') {
158 if (j
> 0 && canon_path
[j
-1] == '/') {
159 /* ignore redundant slash */
161 canon_path
[j
++] = '/';
165 canon_path
[j
++] = g_ascii_tolower(path
[i
]);
167 canon_path
[j
++] = path
[i
];
171 canon_path
[j
] = '\0';
173 if (g_path_is_absolute(canon_path
)
174 && g_file_test(canon_path
, G_FILE_TEST_IS_REGULAR
)
175 && strstr(canon_path
, "/../") == NULL
) {
183 /* Add a file into to do list */
185 pbl_add_proto_file_to_be_parsed(pbl_descriptor_pool_t
* pool
, const char* filepath
)
189 char* concat_path
= NULL
;
191 /* Try to get the absolute path of the file */
192 if (g_path_is_absolute(filepath
)) {
193 path
= pbl_canonicalize_absolute_filepath(filepath
);
197 /* try to concat with source directories */
198 for (it
= g_queue_peek_head_link(pool
->source_paths
); it
; it
= it
->next
) {
199 concat_path
= g_build_filename((char*)it
->data
, filepath
, NULL
);
200 path
= pbl_canonicalize_absolute_filepath(concat_path
);
207 if (pool
->parser_state
) {
208 /* only happened during parsing an 'import' line of a .proto file */
209 pbl_parser_error(pool
->parser_state
, "file [%s] does not exist!\n", filepath
);
211 /* normally happened during initializing a pool by adding files that need be loaded */
212 pool
->error_cb("Protobuf: file [%s] does not exist!\n", filepath
);
217 if (!g_hash_table_lookup(pool
->proto_files
, path
)) {
218 /* create file descriptor info */
219 pbl_file_descriptor_t
* file
= g_new0(pbl_file_descriptor_t
, 1);
220 file
->filename
= path
;
221 file
->syntax_version
= 2;
222 file
->package_name
= PBL_DEFAULT_PACKAGE_NAME
;
223 file
->package_name_lineno
= -1;
226 /* store in hash table and list */
227 g_hash_table_insert(pool
->proto_files
, path
, file
);
228 g_queue_push_tail(pool
->proto_files_to_be_parsed
, path
);
230 /* The file is already in the proto_files */
236 /* find node according to full_name */
238 pbl_find_node_in_pool(const pbl_descriptor_pool_t
* pool
, const char* full_name
, pbl_node_type_t nodetype
)
243 pbl_node_t
* node
= NULL
;
244 GSList
* names
= NULL
; /* NULL terminated name retrieved from full_name */
247 if (pool
== NULL
|| full_name
== NULL
|| pool
->packages
== NULL
) {
251 if (full_name
[0] == '.') {
252 full_name
++; /* skip leading dot */
255 full_name_buf
= g_strdup(full_name
);
256 len
= (int)strlen(full_name_buf
);
257 /* scan from end to begin, and replace '.' to '\0' */
258 for (i
= len
-1; i
>= 0; i
--) {
259 if (full_name_buf
[i
] == '.' || i
== 0) {
261 /* no dot any more, we search in default package */
262 names
= g_slist_prepend(names
, full_name_buf
);
263 package
= (pbl_node_t
*) g_hash_table_lookup(pool
->packages
, PBL_DEFAULT_PACKAGE_NAME
);
264 } else { /* replace middle dot with '\0' */
265 /* push name at top of names */
266 names
= g_slist_prepend(names
, full_name_buf
+ i
+ 1);
267 full_name_buf
[i
] = 0;
268 /* take 0~i of full_name_buf as package name */
269 package
= (pbl_node_t
*) g_hash_table_lookup(pool
->packages
, full_name_buf
);
273 /* search node in this package */
274 for (it
= names
; (it
&& node
&& node
->children_by_name
); it
= it
->next
) {
275 node
= (pbl_node_t
*) g_hash_table_lookup(node
->children_by_name
, it
->data
);
278 if (it
== NULL
&& node
&& node
->nodetype
== nodetype
) {
289 g_free(full_name_buf
);
293 /* get the full name of node. if it is NULL, it will be built. */
295 // NOLINTNEXTLINE(misc-no-recursion)
296 pbl_get_node_full_name(pbl_node_t
* node
)
298 const char* parent_full_name
;
300 || node
->nodetype
== PBL_UNKNOWN
301 || node
->nodetype
== PBL_OPTIONS
302 || node
->nodetype
== PBL_OPTION
) {
306 if (node
->full_name
) {
307 return node
->full_name
;
310 if (node
->nodetype
== PBL_ONEOF
) {
311 if (!check_node_depth(node
)) {
314 return pbl_get_node_full_name(node
->parent
);
317 if (node
->nodetype
== PBL_PACKAGE
) {
318 node
->full_name
= g_strdup(node
->name
);
320 parent_full_name
= pbl_get_node_full_name(node
->parent
);
321 if (parent_full_name
&& parent_full_name
[0] != 0) {
322 node
->full_name
= g_strconcat(parent_full_name
, ".", node
->name
, NULL
);
324 node
->full_name
= g_strdup(node
->name
);
328 return node
->full_name
;
331 /* try to find node globally or in the context or parents (message or package) of the context */
332 static const pbl_node_t
*
333 pbl_find_node_in_context(const pbl_node_t
* context
, const char* name
, pbl_node_type_t nodetype
)
335 const pbl_node_t
* node
= NULL
;
336 pbl_descriptor_pool_t
* pool
= NULL
;
340 if (context
== NULL
|| name
== NULL
) {
344 if (name
[0] == '.') {
345 /* A leading '.' (for example, .foo.bar.Baz) means to start from the outermost scope. */
346 if (context
->file
&& context
->file
->pool
) {
347 return pbl_find_node_in_pool(context
->file
->pool
, name
, nodetype
);
355 pool
= context
->file
->pool
;
358 /* try find node in the context or parents (message or package) of the context */
361 parent_name
= g_strdup(pbl_get_node_full_name((pbl_node_t
*) context
));
362 remaining
= (int)strlen(parent_name
);
363 while (remaining
> 0) {
364 full_name
= g_strconcat(parent_name
, ".", name
, NULL
);
365 node
= pbl_find_node_in_pool(pool
, full_name
, nodetype
);
371 /* scan from end to begin, and replace first '.' to '\0' */
372 for (remaining
--; remaining
> 0; remaining
--) {
373 if (parent_name
[remaining
] == '.') {
374 /* found a potential parent node name */
375 parent_name
[remaining
] = '\0';
376 break; /* break from the 'for' loop, continue 'while' loop */
382 /* try find node in pool directly */
383 return pbl_find_node_in_pool(pool
, name
, nodetype
);
389 /* like descriptor_pool::FindMethodByName */
390 const pbl_method_descriptor_t
*
391 pbl_message_descriptor_pool_FindMethodByName(const pbl_descriptor_pool_t
* pool
, const char* full_name
)
393 pbl_node_t
* n
= pbl_find_node_in_pool(pool
, full_name
, PBL_METHOD
);
394 return n
? (pbl_method_descriptor_t
*)n
: NULL
;
397 /* like MethodDescriptor::name() */
399 pbl_method_descriptor_name(const pbl_method_descriptor_t
* method
)
401 return pbl_get_node_name((pbl_node_t
*)method
);
404 /* like MethodDescriptor::full_name() */
406 pbl_method_descriptor_full_name(const pbl_method_descriptor_t
* method
)
408 return pbl_get_node_full_name((pbl_node_t
*)method
);
411 /* like MethodDescriptor::input_type() */
412 const pbl_message_descriptor_t
*
413 pbl_method_descriptor_input_type(const pbl_method_descriptor_t
* method
)
415 const pbl_node_t
* n
= pbl_find_node_in_context((pbl_node_t
*)method
, method
->in_msg_type
, PBL_MESSAGE
);
416 return n
? (const pbl_message_descriptor_t
*)n
: NULL
;
419 /* like MethodDescriptor::output_type() */
420 const pbl_message_descriptor_t
*
421 pbl_method_descriptor_output_type(const pbl_method_descriptor_t
* method
)
423 const pbl_node_t
* n
= pbl_find_node_in_context((pbl_node_t
*)method
, method
->out_msg_type
, PBL_MESSAGE
);
424 return n
? (const pbl_message_descriptor_t
*)n
: NULL
;
427 /* like descriptor_pool::FindMessageTypeByName() */
428 const pbl_message_descriptor_t
*
429 pbl_message_descriptor_pool_FindMessageTypeByName(const pbl_descriptor_pool_t
* pool
, const char* name
)
431 pbl_node_t
* n
= pbl_find_node_in_pool(pool
, name
, PBL_MESSAGE
);
432 return n
? (pbl_message_descriptor_t
*)n
: NULL
;
435 /* like Descriptor::name() */
437 pbl_message_descriptor_name(const pbl_message_descriptor_t
* message
)
439 return pbl_get_node_name((pbl_node_t
*)message
);
442 /* like Descriptor::full_name() */
444 pbl_message_descriptor_full_name(const pbl_message_descriptor_t
* message
)
446 return pbl_get_node_full_name((pbl_node_t
*)message
);
449 /* like Descriptor::field_count() */
451 pbl_message_descriptor_field_count(const pbl_message_descriptor_t
* message
)
453 return (message
&& message
->fields
) ? g_queue_get_length(message
->fields
) : 0;
456 /* like Descriptor::field() */
457 const pbl_field_descriptor_t
*
458 pbl_message_descriptor_field(const pbl_message_descriptor_t
* message
, int field_index
)
460 return (message
&& message
->fields
) ? (pbl_field_descriptor_t
*) g_queue_peek_nth(message
->fields
, field_index
) : NULL
;
463 /* like Descriptor::FindFieldByNumber() */
464 const pbl_field_descriptor_t
*
465 pbl_message_descriptor_FindFieldByNumber(const pbl_message_descriptor_t
* message
, int number
)
467 if (message
&& message
->fields_by_number
) {
468 return (pbl_field_descriptor_t
*) g_hash_table_lookup(message
->fields_by_number
, GINT_TO_POINTER(number
));
474 /* like Descriptor::FindFieldByName() */
475 const pbl_field_descriptor_t
*
476 pbl_message_descriptor_FindFieldByName(const pbl_message_descriptor_t
* message
, const char* name
)
478 if (message
&& ((pbl_node_t
*)message
)->children_by_name
) {
479 return (pbl_field_descriptor_t
*) g_hash_table_lookup(((pbl_node_t
*)message
)->children_by_name
, name
);
485 /* like FieldDescriptor::full_name() */
487 pbl_field_descriptor_full_name(const pbl_field_descriptor_t
* field
)
489 return pbl_get_node_full_name((pbl_node_t
*)field
);
492 /* like FieldDescriptor::name() */
494 pbl_field_descriptor_name(const pbl_field_descriptor_t
* field
)
496 return pbl_get_node_name((pbl_node_t
*)field
);
499 /* like FieldDescriptor::number() */
501 pbl_field_descriptor_number(const pbl_field_descriptor_t
* field
)
503 return GPOINTER_TO_INT(field
->number
);
506 /* like FieldDescriptor::type() */
508 pbl_field_descriptor_type(const pbl_field_descriptor_t
* field
)
510 const pbl_node_t
* node
;
511 if (field
->type
== PROTOBUF_TYPE_NONE
) {
512 /* try to lookup as ENUM */
513 node
= pbl_find_node_in_context(((pbl_node_t
*)field
)->parent
, field
->type_name
, PBL_ENUM
);
515 ((pbl_field_descriptor_t
*)field
)->type
= PROTOBUF_TYPE_ENUM
;
517 /* try to lookup as MESSAGE */
518 node
= pbl_find_node_in_context(((pbl_node_t
*)field
)->parent
, field
->type_name
, PBL_MESSAGE
);
520 ((pbl_field_descriptor_t
*)field
)->type
= PROTOBUF_TYPE_MESSAGE
;
527 /* like FieldDescriptor::is_repeated() */
529 pbl_field_descriptor_is_repeated(const pbl_field_descriptor_t
* field
)
531 return field
->is_repeated
? 1 : 0;
534 /* like FieldDescriptor::is_packed() */
536 pbl_field_descriptor_is_packed(const pbl_field_descriptor_t
* field
)
538 bool has_packed_option
;
539 bool packed_option_value
;
540 int syntax_version
= ((pbl_node_t
*)field
)->file
->syntax_version
;
542 /* determine packed flag */
543 if (field
->is_repeated
== false) {
546 /* note: field->type may be undetermined until calling pbl_field_descriptor_type() */
547 switch (pbl_field_descriptor_type(field
)) {
548 case PROTOBUF_TYPE_STRING
:
549 case PROTOBUF_TYPE_GROUP
:
550 case PROTOBUF_TYPE_MESSAGE
:
551 case PROTOBUF_TYPE_BYTES
:
553 default: /* only repeated fields of primitive numeric types can be declared "packed". */
554 has_packed_option
= field
->options_node
555 && field
->options_node
->children_by_name
556 && g_hash_table_lookup(field
->options_node
->children_by_name
, "packed");
558 packed_option_value
= (has_packed_option
?
560 ((pbl_option_descriptor_t
*)g_hash_table_lookup(
561 field
->options_node
->children_by_name
, "packed"))->value
, "true") == 0
564 if (syntax_version
== 2) {
565 return packed_option_value
;
566 } else { /* packed default in syntax_version = 3 */
567 return has_packed_option
? packed_option_value
: true;
572 /* like FieldDescriptor::TypeName() */
574 pbl_field_descriptor_TypeName(int field_type
)
576 return val_to_str(field_type
, protobuf_field_type
, "UNKNOWN_FIELD_TYPE(%d)");
579 /* like FieldDescriptor::message_type() type = TYPE_MESSAGE or TYPE_GROUP */
580 const pbl_message_descriptor_t
*
581 pbl_field_descriptor_message_type(const pbl_field_descriptor_t
* field
)
584 if (field
->type
== PROTOBUF_TYPE_MESSAGE
|| field
->type
== PROTOBUF_TYPE_GROUP
) {
585 n
= pbl_find_node_in_context(((pbl_node_t
*)field
)->parent
, field
->type_name
, PBL_MESSAGE
);
586 return n
? (const pbl_message_descriptor_t
*)n
: NULL
;
591 /* like FieldDescriptor::enum_type() type = TYPE_ENUM */
592 const pbl_enum_descriptor_t
*
593 pbl_field_descriptor_enum_type(const pbl_field_descriptor_t
* field
)
596 if (field
->type
== PROTOBUF_TYPE_ENUM
) {
597 n
= pbl_find_node_in_context(((pbl_node_t
*)field
)->parent
, field
->type_name
, PBL_ENUM
);
598 return n
? (const pbl_enum_descriptor_t
*)n
: NULL
;
603 /* like FieldDescriptor::is_required() */
605 pbl_field_descriptor_is_required(const pbl_field_descriptor_t
* field
)
607 return field
->is_required
;
610 /* like FieldDescriptor::has_default_value() */
612 pbl_field_descriptor_has_default_value(const pbl_field_descriptor_t
* field
)
614 return field
->has_default_value
;
617 /* like FieldDescriptor::default_value_int32() */
619 pbl_field_descriptor_default_value_int32(const pbl_field_descriptor_t
* field
)
621 return field
->default_value
.i32
;
624 /* like FieldDescriptor::default_value_int64() */
626 pbl_field_descriptor_default_value_int64(const pbl_field_descriptor_t
* field
)
628 return field
->default_value
.i64
;
631 /* like FieldDescriptor::default_value_uint32() */
633 pbl_field_descriptor_default_value_uint32(const pbl_field_descriptor_t
* field
)
635 return field
->default_value
.u32
;
638 /* like FieldDescriptor::default_value_uint64() */
640 pbl_field_descriptor_default_value_uint64(const pbl_field_descriptor_t
* field
)
642 return field
->default_value
.u64
;
645 /* like FieldDescriptor::default_value_float() */
647 pbl_field_descriptor_default_value_float(const pbl_field_descriptor_t
* field
)
649 return field
->default_value
.f
;
652 /* like FieldDescriptor::default_value_double() */
654 pbl_field_descriptor_default_value_double(const pbl_field_descriptor_t
* field
)
656 return field
->default_value
.d
;
659 /* like FieldDescriptor::default_value_bool() */
661 pbl_field_descriptor_default_value_bool(const pbl_field_descriptor_t
* field
)
663 return field
->default_value
.b
;
666 /* like FieldDescriptor::default_value_string() */
668 pbl_field_descriptor_default_value_string(const pbl_field_descriptor_t
* field
, int* size
)
670 *size
= field
->string_or_bytes_default_value_length
;
671 return field
->default_value
.s
;
674 /* like FieldDescriptor::default_value_enum() */
675 const pbl_enum_value_descriptor_t
*
676 pbl_field_descriptor_default_value_enum(const pbl_field_descriptor_t
* field
)
678 const pbl_enum_descriptor_t
* enum_desc
;
680 if (pbl_field_descriptor_type(field
) == PROTOBUF_TYPE_ENUM
681 && field
->default_value
.e
== NULL
&& (enum_desc
= pbl_field_descriptor_enum_type(field
))) {
683 if (field
->orig_default_value
) {
684 /* find enum_value according to the name of default value */
685 ((pbl_field_descriptor_t
*)field
)->default_value
.e
= pbl_enum_descriptor_FindValueByName(enum_desc
, field
->orig_default_value
);
687 /* the default value is the first defined enum value */
688 ((pbl_field_descriptor_t
*)field
)->default_value
.e
= pbl_enum_descriptor_value(enum_desc
, 0);
692 return field
->default_value
.e
;
695 /* like EnumDescriptor::name() */
697 pbl_enum_descriptor_name(const pbl_enum_descriptor_t
* anEnum
)
699 return pbl_get_node_name((pbl_node_t
*)anEnum
);
702 /* like EnumDescriptor::full_name() */
704 pbl_enum_descriptor_full_name(const pbl_enum_descriptor_t
* anEnum
)
706 return pbl_get_node_full_name((pbl_node_t
*)anEnum
);
709 /* like EnumDescriptor::value_count() */
711 pbl_enum_descriptor_value_count(const pbl_enum_descriptor_t
* anEnum
)
713 return (anEnum
&& anEnum
->values
) ? g_queue_get_length(anEnum
->values
) : 0;
716 /* like EnumDescriptor::value() */
717 const pbl_enum_value_descriptor_t
*
718 pbl_enum_descriptor_value(const pbl_enum_descriptor_t
* anEnum
, int value_index
)
720 return (anEnum
&& anEnum
->values
) ? (pbl_enum_value_descriptor_t
*) g_queue_peek_nth(anEnum
->values
, value_index
) : NULL
;
723 /* like EnumDescriptor::FindValueByNumber() */
724 const pbl_enum_value_descriptor_t
*
725 pbl_enum_descriptor_FindValueByNumber(const pbl_enum_descriptor_t
* anEnum
, int number
)
727 if (anEnum
&& anEnum
->values_by_number
) {
728 return (pbl_enum_value_descriptor_t
*) g_hash_table_lookup(anEnum
->values_by_number
, GINT_TO_POINTER(number
));
734 /* like EnumDescriptor::FindValueByName() */
735 const pbl_enum_value_descriptor_t
*
736 pbl_enum_descriptor_FindValueByName(const pbl_enum_descriptor_t
* anEnum
, const char* name
)
738 if (anEnum
&& ((pbl_node_t
*)anEnum
)->children_by_name
) {
739 return (pbl_enum_value_descriptor_t
*)g_hash_table_lookup(((pbl_node_t
*)anEnum
)->children_by_name
, name
);
745 /* like EnumValueDescriptor::name() */
747 pbl_enum_value_descriptor_name(const pbl_enum_value_descriptor_t
* enumValue
)
749 return pbl_get_node_name((pbl_node_t
*)enumValue
);
752 /* like EnumValueDescriptor::full_name() */
754 pbl_enum_value_descriptor_full_name(const pbl_enum_value_descriptor_t
* enumValue
)
756 return pbl_get_node_full_name((pbl_node_t
*)enumValue
);
759 /* like EnumValueDescriptor::number() */
761 pbl_enum_value_descriptor_number(const pbl_enum_value_descriptor_t
* enumValue
)
763 return GPOINTER_TO_INT(enumValue
->number
);
767 // NOLINTNEXTLINE(misc-no-recursion)
768 pbl_traverse_sub_tree(const pbl_node_t
* node
, void (*cb
)(const pbl_message_descriptor_t
*, void*), void* userdata
)
775 if (node
->nodetype
== PBL_MESSAGE
) {
776 (*cb
)((const pbl_message_descriptor_t
*) node
, userdata
);
779 if (node
->children
) {
780 if (!check_node_depth(node
)) {
783 for (it
= g_queue_peek_head_link(node
->children
); it
; it
= it
->next
) {
784 pbl_traverse_sub_tree((const pbl_node_t
*) it
->data
, cb
, userdata
);
789 /* visit all message in this pool */
791 pbl_foreach_message(const pbl_descriptor_pool_t
* pool
, void (*cb
)(const pbl_message_descriptor_t
*, void*), void* userdata
)
795 g_hash_table_iter_init (&it
, pool
->packages
);
796 while (g_hash_table_iter_next (&it
, &key
, &value
)) {
797 pbl_traverse_sub_tree((const pbl_node_t
*)value
, cb
, userdata
);
803 * Following are tree building functions that should only be invoked by protobuf_lang parser.
807 pbl_init_node(pbl_node_t
* node
, pbl_file_descriptor_t
* file
, int lineno
, pbl_node_type_t nodetype
, const char* name
)
809 node
->nodetype
= nodetype
;
810 node
->name
= g_strdup(name
);
812 node
->lineno
= (lineno
> -1) ? lineno
: -1;
815 /* create a normal node */
817 pbl_create_node(pbl_file_descriptor_t
* file
, int lineno
, pbl_node_type_t nodetype
, const char* name
)
819 pbl_node_t
* node
= NULL
;
822 case PBL_METHOD
: /* should use pbl_create_method_node() */
823 case PBL_FIELD
: /* should use pbl_create_field_node() */
824 case PBL_MAP_FIELD
: /* should use pbl_create_map_field_node() */
825 case PBL_ENUM_VALUE
: /* should use pbl_create_enum_value_node() */
826 case PBL_OPTION
: /* should use pbl_create_option_node() */
829 node
= (pbl_node_t
*) g_malloc0(sizeof(pbl_message_descriptor_t
));
832 node
= (pbl_node_t
*) g_malloc0(sizeof(pbl_enum_descriptor_t
));
835 node
= g_new0(pbl_node_t
, 1);
837 pbl_init_node(node
, file
, lineno
, nodetype
, name
);
842 pbl_set_node_name(pbl_node_t
* node
, int lineno
, const char* newname
)
845 node
->name
= g_strdup(newname
);
847 node
->lineno
= lineno
;
852 static pbl_option_descriptor_t
*
853 pbl_get_option_by_name(pbl_node_t
* options
, const char* name
)
855 if (options
&& options
->children_by_name
) {
856 return (pbl_option_descriptor_t
*)g_hash_table_lookup(options
->children_by_name
, name
);
862 /* create a method (rpc or stream of service) node */
863 pbl_node_t
* pbl_create_method_node(pbl_file_descriptor_t
* file
, int lineno
,
864 const char* name
, const char* in_msg_type
,
865 bool in_is_stream
, const char* out_msg_type
, bool out_is_stream
)
867 pbl_method_descriptor_t
* node
= g_new0(pbl_method_descriptor_t
, 1);
868 pbl_init_node(&node
->basic_info
, file
, lineno
, PBL_METHOD
, name
);
870 node
->in_msg_type
= g_strdup(in_msg_type
);
871 node
->in_is_stream
= in_is_stream
;
872 node
->out_msg_type
= g_strdup(out_msg_type
);
873 node
->out_is_stream
= out_is_stream
;
875 return (pbl_node_t
*)node
;
878 /* Get type simple type enum value according to the type name.
879 Return 0 means undetermined. */
881 pbl_get_simple_type_enum_value_by_typename(const char* type_name
)
883 int i
= str_to_val(type_name
, protobuf_field_type
, 0);
884 if (i
== PROTOBUF_TYPE_GROUP
|| i
== PROTOBUF_TYPE_MESSAGE
|| i
== PROTOBUF_TYPE_ENUM
) {
885 i
= PROTOBUF_TYPE_NONE
; /* complex type will find after parsing */
891 /* create a field node */
892 pbl_node_t
* pbl_create_field_node(pbl_file_descriptor_t
* file
, int lineno
, const char* label
,
893 const char* type_name
, const char* name
, int number
, pbl_node_t
* options
)
895 pbl_option_descriptor_t
* default_option
;
896 pbl_field_descriptor_t
* node
= g_new0(pbl_field_descriptor_t
, 1);
897 pbl_init_node(&node
->basic_info
, file
, lineno
, PBL_FIELD
, name
);
899 node
->number
= number
;
900 node
->options_node
= options
;
901 node
->is_repeated
= (g_strcmp0(label
, "repeated") == 0);
902 node
->type_name
= g_strdup(type_name
);
903 /* type 0 means undetermined, it will be determined on
904 calling pbl_field_descriptor_type() later */
905 node
->type
= pbl_get_simple_type_enum_value_by_typename(type_name
);
906 node
->is_required
= (g_strcmp0(label
, "required") == 0);
908 /* Try to get default value for proto2.
909 * There is nothing to do for proto3, because the default value
910 * is zero or false in proto3.
912 default_option
= pbl_get_option_by_name(options
, "default");
913 if (default_option
&& default_option
->value
) {
914 node
->has_default_value
= true;
915 node
->orig_default_value
= g_strdup(default_option
->value
);
916 /* get default value for simple type */
919 case PROTOBUF_TYPE_INT32
:
920 case PROTOBUF_TYPE_SINT32
:
921 case PROTOBUF_TYPE_SFIXED32
:
922 sscanf(node
->orig_default_value
, "%" SCNd32
, &node
->default_value
.i32
);
925 case PROTOBUF_TYPE_INT64
:
926 case PROTOBUF_TYPE_SINT64
:
927 case PROTOBUF_TYPE_SFIXED64
:
928 node
->default_value
.i64
= g_ascii_strtoll(node
->orig_default_value
, NULL
, 10);
931 case PROTOBUF_TYPE_UINT32
:
932 case PROTOBUF_TYPE_FIXED32
:
933 sscanf(node
->orig_default_value
, "%" SCNu32
, &node
->default_value
.u32
);
936 case PROTOBUF_TYPE_UINT64
:
937 case PROTOBUF_TYPE_FIXED64
:
938 node
->default_value
.u64
= g_ascii_strtoull(node
->orig_default_value
, NULL
, 10);
941 case PROTOBUF_TYPE_BOOL
:
942 node
->default_value
.b
= (g_strcmp0(node
->orig_default_value
, "true") == 0);
945 case PROTOBUF_TYPE_DOUBLE
:
946 node
->default_value
.d
= g_ascii_strtod(node
->orig_default_value
, NULL
);
949 case PROTOBUF_TYPE_FLOAT
:
950 node
->default_value
.f
= (float) g_ascii_strtod(node
->orig_default_value
, NULL
);
953 case PROTOBUF_TYPE_STRING
:
954 case PROTOBUF_TYPE_BYTES
:
955 node
->default_value
.s
= protobuf_string_unescape(node
->orig_default_value
, &node
->string_or_bytes_default_value_length
);
959 /* The default value of ENUM type will be generated
960 * in pbl_field_descriptor_default_value_enum().
961 * Message or group will be ignore.
967 return (pbl_node_t
*)node
;
970 /* create a map field node */
971 pbl_node_t
* pbl_create_map_field_node(pbl_file_descriptor_t
* file
, int lineno
,
972 const char* name
, int number
, pbl_node_t
* options
)
974 pbl_field_descriptor_t
* node
= g_new0(pbl_field_descriptor_t
, 1);
975 pbl_init_node(&node
->basic_info
, file
, lineno
, PBL_MAP_FIELD
, name
);
977 node
->number
= number
;
978 node
->type_name
= g_strconcat(name
, "MapEntry", NULL
);
979 node
->type
= PROTOBUF_TYPE_MESSAGE
;
980 node
->is_repeated
= true;
981 node
->options_node
= options
;
983 return (pbl_node_t
*)node
;
986 /* create an enumeration field node */
988 pbl_create_enum_value_node(pbl_file_descriptor_t
* file
, int lineno
, const char* name
, int number
)
990 pbl_enum_value_descriptor_t
* node
= g_new0(pbl_enum_value_descriptor_t
, 1);
991 pbl_init_node(&node
->basic_info
, file
, lineno
, PBL_ENUM_VALUE
, name
);
993 node
->number
= number
;
994 return (pbl_node_t
*)node
;
997 /* create an option node */
998 pbl_node_t
* pbl_create_option_node(pbl_file_descriptor_t
* file
, int lineno
,
999 const char* name
, const char* value
)
1001 pbl_option_descriptor_t
* node
= g_new0(pbl_option_descriptor_t
, 1);
1002 pbl_init_node(&node
->basic_info
, file
, lineno
, PBL_OPTION
, name
);
1005 node
->value
= g_strdup(value
);
1006 return (pbl_node_t
*)node
;
1009 /* add a node as a child of parent node, and return the parent pointer */
1011 // NOLINTNEXTLINE(misc-no-recursion)
1012 pbl_add_child(pbl_node_t
* parent
, pbl_node_t
* child
)
1014 pbl_node_t
* node
= NULL
;
1015 if (child
== NULL
|| parent
== NULL
) {
1019 if (!check_node_depth(parent
)) {
1023 /* add a message node for mapField first */
1024 if (child
->nodetype
== PBL_MAP_FIELD
) {
1025 node
= pbl_create_node(child
->file
, child
->lineno
, PBL_MESSAGE
, ((pbl_field_descriptor_t
*)child
)->type_name
);
1026 pbl_merge_children(node
, child
);
1027 pbl_add_child(parent
, node
);
1030 child
->parent
= parent
;
1032 /* add child to children list */
1033 if (parent
->children
== NULL
) {
1034 parent
->children
= g_queue_new();
1036 g_queue_push_tail(parent
->children
, child
);
1038 /* add child to children_by_name table */
1039 if (parent
->children_by_name
== NULL
) {
1040 parent
->children_by_name
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, NULL
);
1043 node
= (pbl_node_t
*) g_hash_table_lookup(parent
->children_by_name
, child
->name
);
1044 if (node
&& node
->nodetype
== PBL_OPTION
&& child
->nodetype
== PBL_OPTION
1045 && ((pbl_option_descriptor_t
*)node
)->value
&& ((pbl_option_descriptor_t
*)child
)->value
) {
1046 /* repeated option can be set many times like:
1047 string fieldWithComplexOption5 = 5 [(rules).repeated_int = 1, (rules).repeated_int = 2];
1048 we just merge the old value and new value in format /old_value "," new_value/.
1050 char* oval
= ((pbl_option_descriptor_t
*)node
)->value
;
1051 char* nval
= ((pbl_option_descriptor_t
*)child
)->value
;
1052 ((pbl_option_descriptor_t
*)child
)->value
= g_strconcat(oval
, ",", nval
, NULL
);
1054 } else if (node
&& child
->file
&& parent
->file
1055 && child
->file
->pool
&& child
->file
->pool
->error_cb
1056 /* Let's assume that any set of base types we point at are valid.. */
1057 && !strstr(node
->file
->filename
, "google")) {
1058 child
->file
->pool
->error_cb(
1059 "Protobuf: Warning: \"%s\" of [%s:%d] is already defined in file [%s:%d].\n",
1060 child
->name
, child
->file
->filename
, child
->lineno
, node
->file
->filename
, node
->lineno
);
1063 g_hash_table_insert(parent
->children_by_name
, child
->name
, child
);
1065 if (parent
->nodetype
== PBL_MESSAGE
) {
1066 pbl_message_descriptor_t
* msg
= (pbl_message_descriptor_t
*) parent
;
1068 /* add child to fields_by_number table */
1069 if (child
->nodetype
== PBL_FIELD
|| child
->nodetype
== PBL_MAP_FIELD
) {
1070 if (msg
->fields
== NULL
) {
1071 msg
->fields
= g_queue_new();
1073 g_queue_push_tail(msg
->fields
, child
);
1075 if (msg
->fields_by_number
== NULL
) {
1076 msg
->fields_by_number
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
, NULL
, NULL
);
1078 g_hash_table_insert(msg
->fields_by_number
,
1079 GINT_TO_POINTER(((pbl_field_descriptor_t
*)child
)->number
), child
);
1082 } else if (parent
->nodetype
== PBL_ENUM
&& child
->nodetype
== PBL_ENUM_VALUE
) {
1083 pbl_enum_descriptor_t
* anEnum
= (pbl_enum_descriptor_t
*) parent
;
1085 if (anEnum
->values
== NULL
) {
1086 anEnum
->values
= g_queue_new();
1088 g_queue_push_tail(anEnum
->values
, child
);
1090 /* add child to values_by_number table */
1091 if (anEnum
->values_by_number
== NULL
) {
1092 anEnum
->values_by_number
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
, NULL
, NULL
);
1094 g_hash_table_insert(anEnum
->values_by_number
,
1095 GINT_TO_POINTER(((pbl_enum_value_descriptor_t
*)child
)->number
), child
);
1101 /* merge one('from') node's children to another('to') node, and return the 'to' pointer */
1103 // NOLINTNEXTLINE(misc-no-recursion)
1104 pbl_merge_children(pbl_node_t
* to
, pbl_node_t
* from
)
1109 if (to
== NULL
|| from
== NULL
) {
1113 if (from
->children
) {
1114 for (it
= g_queue_peek_head_link(from
->children
); it
; it
= it
->next
) {
1115 child
= (pbl_node_t
*)it
->data
;
1116 pbl_add_child(to
, child
);
1119 g_queue_free(from
->children
);
1120 from
->children
= NULL
;
1121 if (from
->children_by_name
) {
1122 g_hash_table_destroy(from
->children_by_name
);
1124 from
->children_by_name
= NULL
;
1126 if (from
->nodetype
== PBL_MESSAGE
) {
1127 pbl_message_descriptor_t
* msg
= (pbl_message_descriptor_t
*) from
;
1129 g_queue_free(msg
->fields
);
1132 if (msg
->fields_by_number
) {
1133 g_hash_table_destroy(msg
->fields_by_number
);
1134 msg
->fields_by_number
= NULL
;
1136 } else if (from
->nodetype
== PBL_ENUM
) {
1137 pbl_enum_descriptor_t
* anEnum
= (pbl_enum_descriptor_t
*) from
;
1138 if (anEnum
->values
) {
1139 g_queue_free(anEnum
->values
);
1140 anEnum
->values
= NULL
;
1142 if (anEnum
->values_by_number
) {
1143 g_hash_table_destroy(anEnum
->values_by_number
);
1144 anEnum
->values_by_number
= NULL
;
1152 /* free a pbl_node_t and its children. */
1154 // NOLINTNEXTLINE(misc-no-recursion)
1155 pbl_free_node(void *anode
)
1157 pbl_method_descriptor_t
* method_node
;
1158 pbl_message_descriptor_t
* message_node
;
1159 pbl_field_descriptor_t
* field_node
;
1160 pbl_enum_descriptor_t
* enum_node
;
1161 pbl_option_descriptor_t
* option_node
;
1162 pbl_node_t
* node
= (pbl_node_t
*) anode
;
1164 if (node
== NULL
) return;
1166 switch (node
->nodetype
) {
1168 method_node
= (pbl_method_descriptor_t
*) node
;
1169 g_free(method_node
->in_msg_type
);
1170 g_free(method_node
->out_msg_type
);
1173 message_node
= (pbl_message_descriptor_t
*) node
;
1174 if (message_node
->fields
) {
1175 g_queue_free(message_node
->fields
);
1177 if (message_node
->fields_by_number
) {
1178 g_hash_table_destroy(message_node
->fields_by_number
);
1183 field_node
= (pbl_field_descriptor_t
*) node
;
1184 g_free(field_node
->type_name
);
1185 if (field_node
->orig_default_value
) {
1186 g_free(field_node
->orig_default_value
);
1188 if ((field_node
->type
== PROTOBUF_TYPE_STRING
|| field_node
->type
== PROTOBUF_TYPE_BYTES
)
1189 && field_node
->default_value
.s
) {
1190 g_free(field_node
->default_value
.s
);
1192 if (field_node
->options_node
) {
1193 // We recurse here, but we're limited by depth checks at allocation time
1194 pbl_free_node(field_node
->options_node
);
1198 enum_node
= (pbl_enum_descriptor_t
*) node
;
1199 if (enum_node
->values
) {
1200 g_queue_free(enum_node
->values
);
1202 if (enum_node
->values_by_number
) {
1203 g_hash_table_destroy(enum_node
->values_by_number
);
1207 option_node
= (pbl_option_descriptor_t
*) node
;
1208 g_free(option_node
->value
);
1216 g_free(node
->full_name
);
1217 if (node
->children
) {
1218 g_queue_free_full(node
->children
, pbl_free_node
);
1220 if (node
->children_by_name
) {
1221 g_hash_table_destroy(node
->children_by_name
);
1227 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1232 * indent-tabs-mode: nil
1235 * vi: set shiftwidth=4 tabstop=8 expandtab:
1236 * :indentSize=4:tabSize=8:noTabs=true: