| File: | epan/protobuf_lang_tree.c |
| Warning: | line 1044, column 17 Potential leak of memory pointed to by 'node' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* protobuf_lang_tree.c | ||||
| 2 | * | ||||
| 3 | * Routines of building and reading Protocol Buffers Language grammar tree. | ||||
| 4 | * Copyright 2019, Huang Qiangxiong <[email protected]> | ||||
| 5 | * | ||||
| 6 | * Wireshark - Network traffic analyzer | ||||
| 7 | * By Gerald Combs <[email protected]> | ||||
| 8 | * Copyright 1998 Gerald Combs | ||||
| 9 | * | ||||
| 10 | * SPDX-License-Identifier: GPL-2.0-or-later | ||||
| 11 | */ | ||||
| 12 | |||||
| 13 | #include <string.h> | ||||
| 14 | #include <stddef.h> | ||||
| 15 | #include <stdio.h> | ||||
| 16 | #include <stdlib.h> | ||||
| 17 | #include <limits.h> | ||||
| 18 | #include "protobuf_lang_tree.h" | ||||
| 19 | #include "protobuf-helper.h" /* only for PROTOBUF_TYPE_XXX enumeration */ | ||||
| 20 | |||||
| 21 | #define MAX_PROTOBUF_NODE_DEPTH100 100 | ||||
| 22 | static bool_Bool | ||||
| 23 | check_node_depth(const pbl_node_t *node) | ||||
| 24 | { | ||||
| 25 | int depth = 1; | ||||
| 26 | for (const pbl_node_t *parent = node; parent ; parent = parent->parent) { | ||||
| 27 | depth++; | ||||
| 28 | } | ||||
| 29 | if (depth > MAX_PROTOBUF_NODE_DEPTH100) { | ||||
| 30 | return false0; | ||||
| 31 | } | ||||
| 32 | return true1; | ||||
| 33 | } | ||||
| 34 | |||||
| 35 | extern void | ||||
| 36 | pbl_parser_error(protobuf_lang_state_t *state, const char *fmt, ...); | ||||
| 37 | |||||
| 38 | /* Unescape string to bytes according to: | ||||
| 39 | * | ||||
| 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" | '\' | "'" | '"' ) | ||||
| 45 | * | ||||
| 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() | ||||
| 49 | */ | ||||
| 50 | static char* | ||||
| 51 | protobuf_string_unescape(const char* src, int* size) | ||||
| 52 | { | ||||
| 53 | int src_len; | ||||
| 54 | uint8_t* dst, * q; | ||||
| 55 | const char* p = src; | ||||
| 56 | |||||
| 57 | if (!(src && size && (src_len = (int)strlen(src)))) | ||||
| 58 | return NULL((void*)0); | ||||
| 59 | |||||
| 60 | dst = q = (uint8_t *) g_malloc0(src_len + 1); | ||||
| 61 | |||||
| 62 | while (p < src + src_len && *p) { | ||||
| 63 | if (*p == '\\') { | ||||
| 64 | p++; | ||||
| 65 | |||||
| 66 | if (*p == 'x' || *p == 'X') { /* unescape hex byte */ | ||||
| 67 | *q++ = (uint8_t)strtol(p + 1, (char**)&p, 16); | ||||
| 68 | continue; | ||||
| 69 | } | ||||
| 70 | |||||
| 71 | if (*p >= '0' && *p <= '7') { /* unescape octal byte */ | ||||
| 72 | *q++ = (uint8_t)strtol(p, (char**)&p, 8); | ||||
| 73 | continue; | ||||
| 74 | } | ||||
| 75 | |||||
| 76 | switch (*p) | ||||
| 77 | { | ||||
| 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 '"' */ | ||||
| 86 | *q++ = *p; | ||||
| 87 | break; | ||||
| 88 | } | ||||
| 89 | } else { | ||||
| 90 | *q++ = *p; | ||||
| 91 | } | ||||
| 92 | p++; | ||||
| 93 | } | ||||
| 94 | *q = 0; | ||||
| 95 | *size = (int)(q - dst); | ||||
| 96 | |||||
| 97 | return (char*) dst; | ||||
| 98 | } | ||||
| 99 | |||||
| 100 | /** | ||||
| 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. | ||||
| 105 | */ | ||||
| 106 | void | ||||
| 107 | pbl_reinit_descriptor_pool(pbl_descriptor_pool_t** ppool, const char** directories, pbl_report_error_cb_t error_cb) | ||||
| 108 | { | ||||
| 109 | unsigned i; | ||||
| 110 | |||||
| 111 | pbl_free_pool(*ppool); | ||||
| 112 | pbl_descriptor_pool_t* p = g_new0(pbl_descriptor_pool_t, 1)((pbl_descriptor_pool_t *) g_malloc0_n ((1), sizeof (pbl_descriptor_pool_t ))); | ||||
| 113 | |||||
| 114 | p->source_paths = g_queue_new(); | ||||
| 115 | for (i = 0; directories[i] != NULL((void*)0); i++) { | ||||
| 116 | g_queue_push_tail(p->source_paths, g_strdup(directories[i])g_strdup_inline (directories[i])); | ||||
| 117 | } | ||||
| 118 | |||||
| 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(); | ||||
| 123 | |||||
| 124 | *ppool = p; | ||||
| 125 | } | ||||
| 126 | |||||
| 127 | /* free all memory used by this protocol buffers language pool */ | ||||
| 128 | void | ||||
| 129 | pbl_free_pool(pbl_descriptor_pool_t* pool) | ||||
| 130 | { | ||||
| 131 | if (pool == NULL((void*)0)) return; | ||||
| 132 | |||||
| 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); | ||||
| 137 | |||||
| 138 | g_free(pool); | ||||
| 139 | } | ||||
| 140 | |||||
| 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 '/../'). | ||||
| 149 | */ | ||||
| 150 | static char* | ||||
| 151 | pbl_canonicalize_absolute_filepath(const char* path) | ||||
| 152 | { | ||||
| 153 | int i, j; | ||||
| 154 | char* canon_path = g_new(char, strlen(path) + 1)((char *) g_malloc_n ((strlen(path) + 1), sizeof (char))); | ||||
| 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 */ | ||||
| 160 | } else { | ||||
| 161 | canon_path[j++] = '/'; | ||||
| 162 | } | ||||
| 163 | } else { | ||||
| 164 | #ifdef _WIN32 | ||||
| 165 | canon_path[j++] = g_ascii_tolower(path[i]); | ||||
| 166 | #else | ||||
| 167 | canon_path[j++] = path[i]; | ||||
| 168 | #endif | ||||
| 169 | } | ||||
| 170 | } | ||||
| 171 | canon_path[j] = '\0'; | ||||
| 172 | |||||
| 173 | if (g_path_is_absolute(canon_path) | ||||
| 174 | && g_file_test(canon_path, G_FILE_TEST_IS_REGULAR) | ||||
| 175 | && strstr(canon_path, "/../") == NULL((void*)0)) { | ||||
| 176 | return canon_path; | ||||
| 177 | } else { | ||||
| 178 | g_free(canon_path); | ||||
| 179 | return NULL((void*)0); | ||||
| 180 | } | ||||
| 181 | } | ||||
| 182 | |||||
| 183 | /* Add a file into to do list */ | ||||
| 184 | bool_Bool | ||||
| 185 | pbl_add_proto_file_to_be_parsed(pbl_descriptor_pool_t* pool, const char* filepath) | ||||
| 186 | { | ||||
| 187 | char* path = NULL((void*)0); | ||||
| 188 | GList* it = NULL((void*)0); | ||||
| 189 | char* concat_path = NULL((void*)0); | ||||
| 190 | |||||
| 191 | /* Try to get the absolute path of the file */ | ||||
| 192 | if (g_path_is_absolute(filepath)) { | ||||
| 193 | path = pbl_canonicalize_absolute_filepath(filepath); | ||||
| 194 | } | ||||
| 195 | |||||
| 196 | if (path == NULL((void*)0)) { | ||||
| 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((void*)0)); | ||||
| 200 | path = pbl_canonicalize_absolute_filepath(concat_path); | ||||
| 201 | g_free(concat_path); | ||||
| 202 | if (path) break; | ||||
| 203 | } | ||||
| 204 | } | ||||
| 205 | |||||
| 206 | if (path == NULL((void*)0)) { | ||||
| 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); | ||||
| 210 | } else { | ||||
| 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); | ||||
| 213 | } | ||||
| 214 | return false0; | ||||
| 215 | } | ||||
| 216 | |||||
| 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)((pbl_file_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_file_descriptor_t ))); | ||||
| 220 | file->filename = path; | ||||
| 221 | file->syntax_version = 2; | ||||
| 222 | file->package_name = PBL_DEFAULT_PACKAGE_NAME""; | ||||
| 223 | file->package_name_lineno = -1; | ||||
| 224 | file->pool = pool; | ||||
| 225 | |||||
| 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); | ||||
| 229 | } else { | ||||
| 230 | /* The file is already in the proto_files */ | ||||
| 231 | g_free(path); | ||||
| 232 | } | ||||
| 233 | return true1; | ||||
| 234 | } | ||||
| 235 | |||||
| 236 | /* find node according to full_name */ | ||||
| 237 | static pbl_node_t* | ||||
| 238 | pbl_find_node_in_pool(const pbl_descriptor_pool_t* pool, const char* full_name, pbl_node_type_t nodetype) | ||||
| 239 | { | ||||
| 240 | char* full_name_buf; | ||||
| 241 | int len, i; | ||||
| 242 | pbl_node_t* package; | ||||
| 243 | pbl_node_t* node = NULL((void*)0); | ||||
| 244 | GSList* names = NULL((void*)0); /* NULL terminated name retrieved from full_name */ | ||||
| 245 | GSList* it = NULL((void*)0); | ||||
| 246 | |||||
| 247 | if (pool == NULL((void*)0) || full_name == NULL((void*)0) || pool->packages == NULL((void*)0)) { | ||||
| 248 | return NULL((void*)0); | ||||
| 249 | } | ||||
| 250 | |||||
| 251 | if (full_name[0] == '.') { | ||||
| 252 | full_name++; /* skip leading dot */ | ||||
| 253 | } | ||||
| 254 | |||||
| 255 | full_name_buf = g_strdup(full_name)g_strdup_inline (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) { | ||||
| 260 | if (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); | ||||
| 270 | } | ||||
| 271 | if (package) { | ||||
| 272 | node = package; | ||||
| 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); | ||||
| 276 | } | ||||
| 277 | |||||
| 278 | if (it == NULL((void*)0) && node && node->nodetype == nodetype) { | ||||
| 279 | break; /* found */ | ||||
| 280 | } | ||||
| 281 | node = NULL((void*)0); | ||||
| 282 | } | ||||
| 283 | } | ||||
| 284 | } | ||||
| 285 | |||||
| 286 | if (names) { | ||||
| 287 | g_slist_free(names); | ||||
| 288 | } | ||||
| 289 | g_free(full_name_buf); | ||||
| 290 | return node; | ||||
| 291 | } | ||||
| 292 | |||||
| 293 | /* get the full name of node. if it is NULL, it will be built. */ | ||||
| 294 | const char* | ||||
| 295 | // NOLINTNEXTLINE(misc-no-recursion) | ||||
| 296 | pbl_get_node_full_name(pbl_node_t* node) | ||||
| 297 | { | ||||
| 298 | const char* parent_full_name; | ||||
| 299 | if (node == NULL((void*)0) | ||||
| 300 | || node->nodetype == PBL_UNKNOWN | ||||
| 301 | || node->nodetype == PBL_OPTIONS | ||||
| 302 | || node->nodetype == PBL_OPTION) { | ||||
| 303 | return NULL((void*)0); | ||||
| 304 | } | ||||
| 305 | |||||
| 306 | if (node->full_name) { | ||||
| 307 | return node->full_name; | ||||
| 308 | } | ||||
| 309 | |||||
| 310 | if (node->nodetype == PBL_ONEOF) { | ||||
| 311 | if (!check_node_depth(node)) { | ||||
| 312 | return NULL((void*)0); | ||||
| 313 | } | ||||
| 314 | return pbl_get_node_full_name(node->parent); | ||||
| 315 | } | ||||
| 316 | |||||
| 317 | if (node->nodetype == PBL_PACKAGE) { | ||||
| 318 | node->full_name = g_strdup(node->name)g_strdup_inline (node->name); | ||||
| 319 | } else { | ||||
| 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((void*)0)); | ||||
| 323 | } else { | ||||
| 324 | node->full_name = g_strdup(node->name)g_strdup_inline (node->name); | ||||
| 325 | } | ||||
| 326 | } | ||||
| 327 | |||||
| 328 | return node->full_name; | ||||
| 329 | } | ||||
| 330 | |||||
| 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) | ||||
| 334 | { | ||||
| 335 | const pbl_node_t* node = NULL((void*)0); | ||||
| 336 | pbl_descriptor_pool_t* pool = NULL((void*)0); | ||||
| 337 | char* parent_name; | ||||
| 338 | char* full_name; | ||||
| 339 | |||||
| 340 | if (context == NULL((void*)0) || name == NULL((void*)0)) { | ||||
| 341 | return NULL((void*)0); | ||||
| 342 | } | ||||
| 343 | |||||
| 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); | ||||
| 348 | } else { | ||||
| 349 | return NULL((void*)0); | ||||
| 350 | } | ||||
| 351 | } | ||||
| 352 | |||||
| 353 | /* find pool */ | ||||
| 354 | if (context->file) { | ||||
| 355 | pool = context->file->pool; | ||||
| 356 | } | ||||
| 357 | |||||
| 358 | /* try find node in the context or parents (message or package) of the context */ | ||||
| 359 | if (pool) { | ||||
| 360 | int remaining; | ||||
| 361 | parent_name = g_strdup(pbl_get_node_full_name((pbl_node_t*) context))g_strdup_inline (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((void*)0)); | ||||
| 365 | node = pbl_find_node_in_pool(pool, full_name, nodetype); | ||||
| 366 | g_free(full_name); | ||||
| 367 | if (node) { | ||||
| 368 | g_free(parent_name); | ||||
| 369 | return node; | ||||
| 370 | } | ||||
| 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 */ | ||||
| 377 | } | ||||
| 378 | } | ||||
| 379 | } | ||||
| 380 | g_free(parent_name); | ||||
| 381 | |||||
| 382 | /* try find node in pool directly */ | ||||
| 383 | return pbl_find_node_in_pool(pool, name, nodetype); | ||||
| 384 | } | ||||
| 385 | |||||
| 386 | return NULL((void*)0); | ||||
| 387 | } | ||||
| 388 | |||||
| 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) | ||||
| 392 | { | ||||
| 393 | pbl_node_t* n = pbl_find_node_in_pool(pool, full_name, PBL_METHOD); | ||||
| 394 | return n ? (pbl_method_descriptor_t*)n : NULL((void*)0); | ||||
| 395 | } | ||||
| 396 | |||||
| 397 | /* like MethodDescriptor::name() */ | ||||
| 398 | const char* | ||||
| 399 | pbl_method_descriptor_name(const pbl_method_descriptor_t* method) | ||||
| 400 | { | ||||
| 401 | return pbl_get_node_name((pbl_node_t*)method); | ||||
| 402 | } | ||||
| 403 | |||||
| 404 | /* like MethodDescriptor::full_name() */ | ||||
| 405 | const char* | ||||
| 406 | pbl_method_descriptor_full_name(const pbl_method_descriptor_t* method) | ||||
| 407 | { | ||||
| 408 | return pbl_get_node_full_name((pbl_node_t*)method); | ||||
| 409 | } | ||||
| 410 | |||||
| 411 | /* like MethodDescriptor::input_type() */ | ||||
| 412 | const pbl_message_descriptor_t* | ||||
| 413 | pbl_method_descriptor_input_type(const pbl_method_descriptor_t* method) | ||||
| 414 | { | ||||
| 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((void*)0); | ||||
| 417 | } | ||||
| 418 | |||||
| 419 | /* like MethodDescriptor::output_type() */ | ||||
| 420 | const pbl_message_descriptor_t* | ||||
| 421 | pbl_method_descriptor_output_type(const pbl_method_descriptor_t* method) | ||||
| 422 | { | ||||
| 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((void*)0); | ||||
| 425 | } | ||||
| 426 | |||||
| 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) | ||||
| 430 | { | ||||
| 431 | pbl_node_t* n = pbl_find_node_in_pool(pool, name, PBL_MESSAGE); | ||||
| 432 | return n ? (pbl_message_descriptor_t*)n : NULL((void*)0); | ||||
| 433 | } | ||||
| 434 | |||||
| 435 | /* like Descriptor::name() */ | ||||
| 436 | const char* | ||||
| 437 | pbl_message_descriptor_name(const pbl_message_descriptor_t* message) | ||||
| 438 | { | ||||
| 439 | return pbl_get_node_name((pbl_node_t*)message); | ||||
| 440 | } | ||||
| 441 | |||||
| 442 | /* like Descriptor::full_name() */ | ||||
| 443 | const char* | ||||
| 444 | pbl_message_descriptor_full_name(const pbl_message_descriptor_t* message) | ||||
| 445 | { | ||||
| 446 | return pbl_get_node_full_name((pbl_node_t*)message); | ||||
| 447 | } | ||||
| 448 | |||||
| 449 | /* like Descriptor::field_count() */ | ||||
| 450 | int | ||||
| 451 | pbl_message_descriptor_field_count(const pbl_message_descriptor_t* message) | ||||
| 452 | { | ||||
| 453 | return (message && message->fields) ? g_queue_get_length(message->fields) : 0; | ||||
| 454 | } | ||||
| 455 | |||||
| 456 | /* like Descriptor::field() */ | ||||
| 457 | const pbl_field_descriptor_t* | ||||
| 458 | pbl_message_descriptor_field(const pbl_message_descriptor_t* message, int field_index) | ||||
| 459 | { | ||||
| 460 | return (message && message->fields) ? (pbl_field_descriptor_t*) g_queue_peek_nth(message->fields, field_index) : NULL((void*)0); | ||||
| 461 | } | ||||
| 462 | |||||
| 463 | /* like Descriptor::FindFieldByNumber() */ | ||||
| 464 | const pbl_field_descriptor_t* | ||||
| 465 | pbl_message_descriptor_FindFieldByNumber(const pbl_message_descriptor_t* message, int number) | ||||
| 466 | { | ||||
| 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)((gpointer) (glong) (number))); | ||||
| 469 | } else { | ||||
| 470 | return NULL((void*)0); | ||||
| 471 | } | ||||
| 472 | } | ||||
| 473 | |||||
| 474 | /* like Descriptor::FindFieldByName() */ | ||||
| 475 | const pbl_field_descriptor_t* | ||||
| 476 | pbl_message_descriptor_FindFieldByName(const pbl_message_descriptor_t* message, const char* name) | ||||
| 477 | { | ||||
| 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); | ||||
| 480 | } else { | ||||
| 481 | return NULL((void*)0); | ||||
| 482 | } | ||||
| 483 | } | ||||
| 484 | |||||
| 485 | /* like FieldDescriptor::full_name() */ | ||||
| 486 | const char* | ||||
| 487 | pbl_field_descriptor_full_name(const pbl_field_descriptor_t* field) | ||||
| 488 | { | ||||
| 489 | return pbl_get_node_full_name((pbl_node_t*)field); | ||||
| 490 | } | ||||
| 491 | |||||
| 492 | /* like FieldDescriptor::name() */ | ||||
| 493 | const char* | ||||
| 494 | pbl_field_descriptor_name(const pbl_field_descriptor_t* field) | ||||
| 495 | { | ||||
| 496 | return pbl_get_node_name((pbl_node_t*)field); | ||||
| 497 | } | ||||
| 498 | |||||
| 499 | /* like FieldDescriptor::number() */ | ||||
| 500 | int | ||||
| 501 | pbl_field_descriptor_number(const pbl_field_descriptor_t* field) | ||||
| 502 | { | ||||
| 503 | return GPOINTER_TO_INT(field->number)((gint) (glong) (field->number)); | ||||
| 504 | } | ||||
| 505 | |||||
| 506 | /* like FieldDescriptor::type() */ | ||||
| 507 | int | ||||
| 508 | pbl_field_descriptor_type(const pbl_field_descriptor_t* field) | ||||
| 509 | { | ||||
| 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); | ||||
| 514 | if (node) { | ||||
| 515 | ((pbl_field_descriptor_t*)field)->type = PROTOBUF_TYPE_ENUM; | ||||
| 516 | } else { | ||||
| 517 | /* try to lookup as MESSAGE */ | ||||
| 518 | node = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_MESSAGE); | ||||
| 519 | if (node) { | ||||
| 520 | ((pbl_field_descriptor_t*)field)->type = PROTOBUF_TYPE_MESSAGE; | ||||
| 521 | } | ||||
| 522 | } | ||||
| 523 | } | ||||
| 524 | return field->type; | ||||
| 525 | } | ||||
| 526 | |||||
| 527 | /* like FieldDescriptor::is_repeated() */ | ||||
| 528 | int | ||||
| 529 | pbl_field_descriptor_is_repeated(const pbl_field_descriptor_t* field) | ||||
| 530 | { | ||||
| 531 | return field->is_repeated ? 1 : 0; | ||||
| 532 | } | ||||
| 533 | |||||
| 534 | /* like FieldDescriptor::is_packed() */ | ||||
| 535 | int | ||||
| 536 | pbl_field_descriptor_is_packed(const pbl_field_descriptor_t* field) | ||||
| 537 | { | ||||
| 538 | bool_Bool has_packed_option; | ||||
| 539 | bool_Bool packed_option_value; | ||||
| 540 | int syntax_version = ((pbl_node_t*)field)->file->syntax_version; | ||||
| 541 | |||||
| 542 | /* determine packed flag */ | ||||
| 543 | if (field->is_repeated == false0) { | ||||
| 544 | return false0; | ||||
| 545 | } | ||||
| 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: | ||||
| 552 | return false0; | ||||
| 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"); | ||||
| 557 | |||||
| 558 | packed_option_value = (has_packed_option ? | ||||
| 559 | g_strcmp0( | ||||
| 560 | ((pbl_option_descriptor_t*)g_hash_table_lookup( | ||||
| 561 | field->options_node->children_by_name, "packed"))->value, "true") == 0 | ||||
| 562 | : false0); | ||||
| 563 | |||||
| 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 : true1; | ||||
| 568 | } | ||||
| 569 | } | ||||
| 570 | } | ||||
| 571 | |||||
| 572 | /* like FieldDescriptor::TypeName() */ | ||||
| 573 | const char* | ||||
| 574 | pbl_field_descriptor_TypeName(wmem_allocator_t* scope, int field_type) | ||||
| 575 | { | ||||
| 576 | return val_to_str(scope, field_type, protobuf_field_type, "UNKNOWN_FIELD_TYPE(%d)"); | ||||
| 577 | } | ||||
| 578 | |||||
| 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) | ||||
| 582 | { | ||||
| 583 | const pbl_node_t* n; | ||||
| 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((void*)0); | ||||
| 587 | } | ||||
| 588 | return NULL((void*)0); | ||||
| 589 | } | ||||
| 590 | |||||
| 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) | ||||
| 594 | { | ||||
| 595 | const pbl_node_t* n; | ||||
| 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((void*)0); | ||||
| 599 | } | ||||
| 600 | return NULL((void*)0); | ||||
| 601 | } | ||||
| 602 | |||||
| 603 | /* like FieldDescriptor::is_required() */ | ||||
| 604 | bool_Bool | ||||
| 605 | pbl_field_descriptor_is_required(const pbl_field_descriptor_t* field) | ||||
| 606 | { | ||||
| 607 | return field->is_required; | ||||
| 608 | } | ||||
| 609 | |||||
| 610 | /* like FieldDescriptor::has_default_value() */ | ||||
| 611 | bool_Bool | ||||
| 612 | pbl_field_descriptor_has_default_value(const pbl_field_descriptor_t* field) | ||||
| 613 | { | ||||
| 614 | return field->has_default_value; | ||||
| 615 | } | ||||
| 616 | |||||
| 617 | /* like FieldDescriptor::default_value_int32() */ | ||||
| 618 | int32_t | ||||
| 619 | pbl_field_descriptor_default_value_int32(const pbl_field_descriptor_t* field) | ||||
| 620 | { | ||||
| 621 | return field->default_value.i32; | ||||
| 622 | } | ||||
| 623 | |||||
| 624 | /* like FieldDescriptor::default_value_int64() */ | ||||
| 625 | int64_t | ||||
| 626 | pbl_field_descriptor_default_value_int64(const pbl_field_descriptor_t* field) | ||||
| 627 | { | ||||
| 628 | return field->default_value.i64; | ||||
| 629 | } | ||||
| 630 | |||||
| 631 | /* like FieldDescriptor::default_value_uint32() */ | ||||
| 632 | uint32_t | ||||
| 633 | pbl_field_descriptor_default_value_uint32(const pbl_field_descriptor_t* field) | ||||
| 634 | { | ||||
| 635 | return field->default_value.u32; | ||||
| 636 | } | ||||
| 637 | |||||
| 638 | /* like FieldDescriptor::default_value_uint64() */ | ||||
| 639 | uint64_t | ||||
| 640 | pbl_field_descriptor_default_value_uint64(const pbl_field_descriptor_t* field) | ||||
| 641 | { | ||||
| 642 | return field->default_value.u64; | ||||
| 643 | } | ||||
| 644 | |||||
| 645 | /* like FieldDescriptor::default_value_float() */ | ||||
| 646 | float | ||||
| 647 | pbl_field_descriptor_default_value_float(const pbl_field_descriptor_t* field) | ||||
| 648 | { | ||||
| 649 | return field->default_value.f; | ||||
| 650 | } | ||||
| 651 | |||||
| 652 | /* like FieldDescriptor::default_value_double() */ | ||||
| 653 | double | ||||
| 654 | pbl_field_descriptor_default_value_double(const pbl_field_descriptor_t* field) | ||||
| 655 | { | ||||
| 656 | return field->default_value.d; | ||||
| 657 | } | ||||
| 658 | |||||
| 659 | /* like FieldDescriptor::default_value_bool() */ | ||||
| 660 | bool_Bool | ||||
| 661 | pbl_field_descriptor_default_value_bool(const pbl_field_descriptor_t* field) | ||||
| 662 | { | ||||
| 663 | return field->default_value.b; | ||||
| 664 | } | ||||
| 665 | |||||
| 666 | /* like FieldDescriptor::default_value_string() */ | ||||
| 667 | const char* | ||||
| 668 | pbl_field_descriptor_default_value_string(const pbl_field_descriptor_t* field, int* size) | ||||
| 669 | { | ||||
| 670 | *size = field->string_or_bytes_default_value_length; | ||||
| 671 | return field->default_value.s; | ||||
| 672 | } | ||||
| 673 | |||||
| 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) | ||||
| 677 | { | ||||
| 678 | const pbl_enum_descriptor_t* enum_desc; | ||||
| 679 | |||||
| 680 | if (pbl_field_descriptor_type(field) == PROTOBUF_TYPE_ENUM | ||||
| 681 | && field->default_value.e == NULL((void*)0) && (enum_desc = pbl_field_descriptor_enum_type(field))) { | ||||
| 682 | |||||
| 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); | ||||
| 686 | } else { | ||||
| 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); | ||||
| 689 | } | ||||
| 690 | } | ||||
| 691 | |||||
| 692 | return field->default_value.e; | ||||
| 693 | } | ||||
| 694 | |||||
| 695 | /* like EnumDescriptor::name() */ | ||||
| 696 | const char* | ||||
| 697 | pbl_enum_descriptor_name(const pbl_enum_descriptor_t* anEnum) | ||||
| 698 | { | ||||
| 699 | return pbl_get_node_name((pbl_node_t*)anEnum); | ||||
| 700 | } | ||||
| 701 | |||||
| 702 | /* like EnumDescriptor::full_name() */ | ||||
| 703 | const char* | ||||
| 704 | pbl_enum_descriptor_full_name(const pbl_enum_descriptor_t* anEnum) | ||||
| 705 | { | ||||
| 706 | return pbl_get_node_full_name((pbl_node_t*)anEnum); | ||||
| 707 | } | ||||
| 708 | |||||
| 709 | /* like EnumDescriptor::value_count() */ | ||||
| 710 | int | ||||
| 711 | pbl_enum_descriptor_value_count(const pbl_enum_descriptor_t* anEnum) | ||||
| 712 | { | ||||
| 713 | return (anEnum && anEnum->values) ? g_queue_get_length(anEnum->values) : 0; | ||||
| 714 | } | ||||
| 715 | |||||
| 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) | ||||
| 719 | { | ||||
| 720 | return (anEnum && anEnum->values) ? (pbl_enum_value_descriptor_t*) g_queue_peek_nth(anEnum->values, value_index) : NULL((void*)0); | ||||
| 721 | } | ||||
| 722 | |||||
| 723 | /* like EnumDescriptor::FindValueByNumber() */ | ||||
| 724 | const pbl_enum_value_descriptor_t* | ||||
| 725 | pbl_enum_descriptor_FindValueByNumber(const pbl_enum_descriptor_t* anEnum, int number) | ||||
| 726 | { | ||||
| 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)((gpointer) (glong) (number))); | ||||
| 729 | } else { | ||||
| 730 | return NULL((void*)0); | ||||
| 731 | } | ||||
| 732 | } | ||||
| 733 | |||||
| 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) | ||||
| 737 | { | ||||
| 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); | ||||
| 740 | } else { | ||||
| 741 | return NULL((void*)0); | ||||
| 742 | } | ||||
| 743 | } | ||||
| 744 | |||||
| 745 | /* like EnumValueDescriptor::name() */ | ||||
| 746 | const char* | ||||
| 747 | pbl_enum_value_descriptor_name(const pbl_enum_value_descriptor_t* enumValue) | ||||
| 748 | { | ||||
| 749 | return pbl_get_node_name((pbl_node_t*)enumValue); | ||||
| 750 | } | ||||
| 751 | |||||
| 752 | /* like EnumValueDescriptor::full_name() */ | ||||
| 753 | const char* | ||||
| 754 | pbl_enum_value_descriptor_full_name(const pbl_enum_value_descriptor_t* enumValue) | ||||
| 755 | { | ||||
| 756 | return pbl_get_node_full_name((pbl_node_t*)enumValue); | ||||
| 757 | } | ||||
| 758 | |||||
| 759 | /* like EnumValueDescriptor::number() */ | ||||
| 760 | int | ||||
| 761 | pbl_enum_value_descriptor_number(const pbl_enum_value_descriptor_t* enumValue) | ||||
| 762 | { | ||||
| 763 | return GPOINTER_TO_INT(enumValue->number)((gint) (glong) (enumValue->number)); | ||||
| 764 | } | ||||
| 765 | |||||
| 766 | static void | ||||
| 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) | ||||
| 769 | { | ||||
| 770 | GList* it; | ||||
| 771 | if (node == NULL((void*)0)) { | ||||
| 772 | return; | ||||
| 773 | } | ||||
| 774 | |||||
| 775 | if (node->nodetype == PBL_MESSAGE) { | ||||
| 776 | (*cb)((const pbl_message_descriptor_t*) node, userdata); | ||||
| 777 | } | ||||
| 778 | |||||
| 779 | if (node->children) { | ||||
| 780 | if (!check_node_depth(node)) { | ||||
| 781 | return; | ||||
| 782 | } | ||||
| 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); | ||||
| 785 | } | ||||
| 786 | } | ||||
| 787 | } | ||||
| 788 | |||||
| 789 | /* visit all message in this pool */ | ||||
| 790 | void | ||||
| 791 | pbl_foreach_message(const pbl_descriptor_pool_t* pool, void (*cb)(const pbl_message_descriptor_t*, void*), void* userdata) | ||||
| 792 | { | ||||
| 793 | GHashTableIter it; | ||||
| 794 | gpointer key, value; | ||||
| 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); | ||||
| 798 | } | ||||
| 799 | } | ||||
| 800 | |||||
| 801 | |||||
| 802 | /* | ||||
| 803 | * Following are tree building functions that should only be invoked by protobuf_lang parser. | ||||
| 804 | */ | ||||
| 805 | |||||
| 806 | static void | ||||
| 807 | pbl_init_node(pbl_node_t* node, pbl_file_descriptor_t* file, int lineno, pbl_node_type_t nodetype, const char* name) | ||||
| 808 | { | ||||
| 809 | node->nodetype = nodetype; | ||||
| 810 | node->name = g_strdup(name)g_strdup_inline (name); | ||||
| 811 | node->file = file; | ||||
| 812 | node->lineno = (lineno > -1) ? lineno : -1; | ||||
| 813 | } | ||||
| 814 | |||||
| 815 | /* create a normal node */ | ||||
| 816 | pbl_node_t* | ||||
| 817 | pbl_create_node(pbl_file_descriptor_t* file, int lineno, pbl_node_type_t nodetype, const char* name) | ||||
| 818 | { | ||||
| 819 | pbl_node_t* node = NULL((void*)0); | ||||
| 820 | |||||
| 821 | switch (nodetype) { | ||||
| 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() */ | ||||
| 827 | return NULL((void*)0); | ||||
| 828 | case PBL_MESSAGE: | ||||
| 829 | node = (pbl_node_t*) g_malloc0(sizeof(pbl_message_descriptor_t)); | ||||
| 830 | break; | ||||
| 831 | case PBL_ENUM: | ||||
| 832 | node = (pbl_node_t*) g_malloc0(sizeof(pbl_enum_descriptor_t)); | ||||
| 833 | break; | ||||
| 834 | default: | ||||
| 835 | node = g_new0(pbl_node_t, 1)((pbl_node_t *) g_malloc0_n ((1), sizeof (pbl_node_t))); | ||||
| 836 | } | ||||
| 837 | pbl_init_node(node, file, lineno, nodetype, name); | ||||
| 838 | return node; | ||||
| 839 | } | ||||
| 840 | |||||
| 841 | pbl_node_t* | ||||
| 842 | pbl_set_node_name(pbl_node_t* node, int lineno, const char* newname) | ||||
| 843 | { | ||||
| 844 | g_free(node->name); | ||||
| 845 | node->name = g_strdup(newname)g_strdup_inline (newname); | ||||
| 846 | if (lineno > -1) { | ||||
| 847 | node->lineno = lineno; | ||||
| 848 | } | ||||
| 849 | return node; | ||||
| 850 | } | ||||
| 851 | |||||
| 852 | static pbl_option_descriptor_t* | ||||
| 853 | pbl_get_option_by_name(pbl_node_t* options, const char* name) | ||||
| 854 | { | ||||
| 855 | if (options && options->children_by_name) { | ||||
| 856 | return (pbl_option_descriptor_t*)g_hash_table_lookup(options->children_by_name, name); | ||||
| 857 | } else { | ||||
| 858 | return NULL((void*)0); | ||||
| 859 | } | ||||
| 860 | } | ||||
| 861 | |||||
| 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_Bool in_is_stream, const char* out_msg_type, bool_Bool out_is_stream) | ||||
| 866 | { | ||||
| 867 | pbl_method_descriptor_t* node = g_new0(pbl_method_descriptor_t, 1)((pbl_method_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_method_descriptor_t ))); | ||||
| 868 | pbl_init_node(&node->basic_info, file, lineno, PBL_METHOD, name); | ||||
| 869 | |||||
| 870 | node->in_msg_type = g_strdup(in_msg_type)g_strdup_inline (in_msg_type); | ||||
| 871 | node->in_is_stream = in_is_stream; | ||||
| 872 | node->out_msg_type = g_strdup(out_msg_type)g_strdup_inline (out_msg_type); | ||||
| 873 | node->out_is_stream = out_is_stream; | ||||
| 874 | |||||
| 875 | return (pbl_node_t*)node; | ||||
| 876 | } | ||||
| 877 | |||||
| 878 | /* Get type simple type enum value according to the type name. | ||||
| 879 | Return 0 means undetermined. */ | ||||
| 880 | static int | ||||
| 881 | pbl_get_simple_type_enum_value_by_typename(const char* type_name) | ||||
| 882 | { | ||||
| 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 */ | ||||
| 886 | } | ||||
| 887 | |||||
| 888 | return i; | ||||
| 889 | } | ||||
| 890 | |||||
| 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) | ||||
| 894 | { | ||||
| 895 | pbl_option_descriptor_t* default_option; | ||||
| 896 | pbl_field_descriptor_t* node = g_new0(pbl_field_descriptor_t, 1)((pbl_field_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_field_descriptor_t ))); | ||||
| 897 | pbl_init_node(&node->basic_info, file, lineno, PBL_FIELD, name); | ||||
| 898 | |||||
| 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)g_strdup_inline (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); | ||||
| 907 | |||||
| 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. | ||||
| 911 | */ | ||||
| 912 | default_option = pbl_get_option_by_name(options, "default"); | ||||
| 913 | if (default_option && default_option->value) { | ||||
| 914 | node->has_default_value = true1; | ||||
| 915 | node->orig_default_value = g_strdup(default_option->value)g_strdup_inline (default_option->value); | ||||
| 916 | /* get default value for simple type */ | ||||
| 917 | switch (node->type) | ||||
| 918 | { | ||||
| 919 | case PROTOBUF_TYPE_INT32: | ||||
| 920 | case PROTOBUF_TYPE_SINT32: | ||||
| 921 | case PROTOBUF_TYPE_SFIXED32: | ||||
| 922 | sscanf(node->orig_default_value, "%" SCNd32"d", &node->default_value.i32); | ||||
| 923 | break; | ||||
| 924 | |||||
| 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((void*)0), 10); | ||||
| 929 | break; | ||||
| 930 | |||||
| 931 | case PROTOBUF_TYPE_UINT32: | ||||
| 932 | case PROTOBUF_TYPE_FIXED32: | ||||
| 933 | sscanf(node->orig_default_value, "%" SCNu32"u", &node->default_value.u32); | ||||
| 934 | break; | ||||
| 935 | |||||
| 936 | case PROTOBUF_TYPE_UINT64: | ||||
| 937 | case PROTOBUF_TYPE_FIXED64: | ||||
| 938 | node->default_value.u64 = g_ascii_strtoull(node->orig_default_value, NULL((void*)0), 10); | ||||
| 939 | break; | ||||
| 940 | |||||
| 941 | case PROTOBUF_TYPE_BOOL: | ||||
| 942 | node->default_value.b = (g_strcmp0(node->orig_default_value, "true") == 0); | ||||
| 943 | break; | ||||
| 944 | |||||
| 945 | case PROTOBUF_TYPE_DOUBLE: | ||||
| 946 | node->default_value.d = g_ascii_strtod(node->orig_default_value, NULL((void*)0)); | ||||
| 947 | break; | ||||
| 948 | |||||
| 949 | case PROTOBUF_TYPE_FLOAT: | ||||
| 950 | node->default_value.f = (float) g_ascii_strtod(node->orig_default_value, NULL((void*)0)); | ||||
| 951 | break; | ||||
| 952 | |||||
| 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); | ||||
| 956 | break; | ||||
| 957 | |||||
| 958 | default: | ||||
| 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. | ||||
| 962 | */ | ||||
| 963 | break; | ||||
| 964 | } | ||||
| 965 | } | ||||
| 966 | |||||
| 967 | return (pbl_node_t*)node; | ||||
| 968 | } | ||||
| 969 | |||||
| 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) | ||||
| 973 | { | ||||
| 974 | pbl_field_descriptor_t* node = g_new0(pbl_field_descriptor_t, 1)((pbl_field_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_field_descriptor_t ))); | ||||
| 975 | pbl_init_node(&node->basic_info, file, lineno, PBL_MAP_FIELD, name); | ||||
| 976 | |||||
| 977 | node->number = number; | ||||
| 978 | node->type_name = g_strconcat(name, "MapEntry", NULL((void*)0)); | ||||
| 979 | node->type = PROTOBUF_TYPE_MESSAGE; | ||||
| 980 | node->is_repeated = true1; | ||||
| 981 | node->options_node = options; | ||||
| 982 | |||||
| 983 | return (pbl_node_t*)node; | ||||
| 984 | } | ||||
| 985 | |||||
| 986 | /* create an enumeration field node */ | ||||
| 987 | pbl_node_t* | ||||
| 988 | pbl_create_enum_value_node(pbl_file_descriptor_t* file, int lineno, const char* name, int number) | ||||
| 989 | { | ||||
| 990 | pbl_enum_value_descriptor_t* node = g_new0(pbl_enum_value_descriptor_t, 1)((pbl_enum_value_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_enum_value_descriptor_t ))); | ||||
| 991 | pbl_init_node(&node->basic_info, file, lineno, PBL_ENUM_VALUE, name); | ||||
| 992 | |||||
| 993 | node->number = number; | ||||
| 994 | return (pbl_node_t*)node; | ||||
| 995 | } | ||||
| 996 | |||||
| 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) | ||||
| 1000 | { | ||||
| 1001 | pbl_option_descriptor_t* node = g_new0(pbl_option_descriptor_t, 1)((pbl_option_descriptor_t *) g_malloc0_n ((1), sizeof (pbl_option_descriptor_t ))); | ||||
| 1002 | pbl_init_node(&node->basic_info, file, lineno, PBL_OPTION, name); | ||||
| 1003 | |||||
| 1004 | if (value) | ||||
| 1005 | node->value = g_strdup(value)g_strdup_inline (value); | ||||
| 1006 | return (pbl_node_t*)node; | ||||
| 1007 | } | ||||
| 1008 | |||||
| 1009 | /* add a node as a child of parent node, and return the parent pointer */ | ||||
| 1010 | pbl_node_t* | ||||
| 1011 | // NOLINTNEXTLINE(misc-no-recursion) | ||||
| 1012 | pbl_add_child(pbl_node_t* parent, pbl_node_t* child) | ||||
| 1013 | { | ||||
| 1014 | pbl_node_t* node = NULL((void*)0); | ||||
| 1015 | if (child
| ||||
| |||||
| 1016 | return parent; | ||||
| 1017 | } | ||||
| 1018 | |||||
| 1019 | if (!check_node_depth(parent)) { | ||||
| 1020 | return NULL((void*)0); | ||||
| 1021 | } | ||||
| 1022 | |||||
| 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); | ||||
| 1028 | } | ||||
| 1029 | |||||
| 1030 | child->parent = parent; | ||||
| 1031 | |||||
| 1032 | /* add child to children list */ | ||||
| 1033 | if (parent->children == NULL((void*)0)) { | ||||
| 1034 | parent->children = g_queue_new(); | ||||
| 1035 | } | ||||
| 1036 | g_queue_push_tail(parent->children, child); | ||||
| 1037 | |||||
| 1038 | /* add child to children_by_name table */ | ||||
| 1039 | if (parent->children_by_name == NULL((void*)0)) { | ||||
| 1040 | parent->children_by_name = g_hash_table_new_full(g_str_hash, g_str_equal, NULL((void*)0), NULL((void*)0)); | ||||
| 1041 | } | ||||
| 1042 | |||||
| 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/. | ||||
| 1049 | */ | ||||
| 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((void*)0)); | ||||
| 1053 | g_free(nval); | ||||
| 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); | ||||
| 1061 | } | ||||
| 1062 | |||||
| 1063 | g_hash_table_insert(parent->children_by_name, child->name, child); | ||||
| 1064 | |||||
| 1065 | if (parent->nodetype == PBL_MESSAGE) { | ||||
| 1066 | pbl_message_descriptor_t* msg = (pbl_message_descriptor_t*) parent; | ||||
| 1067 | |||||
| 1068 | /* add child to fields_by_number table */ | ||||
| 1069 | if (child->nodetype == PBL_FIELD || child->nodetype == PBL_MAP_FIELD) { | ||||
| 1070 | if (msg->fields == NULL((void*)0)) { | ||||
| 1071 | msg->fields = g_queue_new(); | ||||
| 1072 | } | ||||
| 1073 | g_queue_push_tail(msg->fields, child); | ||||
| 1074 | |||||
| 1075 | if (msg->fields_by_number == NULL((void*)0)) { | ||||
| 1076 | msg->fields_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL((void*)0), NULL((void*)0)); | ||||
| 1077 | } | ||||
| 1078 | g_hash_table_insert(msg->fields_by_number, | ||||
| 1079 | GINT_TO_POINTER(((pbl_field_descriptor_t*)child)->number)((gpointer) (glong) (((pbl_field_descriptor_t*)child)->number )), child); | ||||
| 1080 | } | ||||
| 1081 | |||||
| 1082 | } else if (parent->nodetype == PBL_ENUM && child->nodetype == PBL_ENUM_VALUE) { | ||||
| 1083 | pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) parent; | ||||
| 1084 | |||||
| 1085 | if (anEnum->values == NULL((void*)0)) { | ||||
| 1086 | anEnum->values = g_queue_new(); | ||||
| 1087 | } | ||||
| 1088 | g_queue_push_tail(anEnum->values, child); | ||||
| 1089 | |||||
| 1090 | /* add child to values_by_number table */ | ||||
| 1091 | if (anEnum->values_by_number == NULL((void*)0)) { | ||||
| 1092 | anEnum->values_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL((void*)0), NULL((void*)0)); | ||||
| 1093 | } | ||||
| 1094 | g_hash_table_insert(anEnum->values_by_number, | ||||
| 1095 | GINT_TO_POINTER(((pbl_enum_value_descriptor_t*)child)->number)((gpointer) (glong) (((pbl_enum_value_descriptor_t*)child)-> number)), child); | ||||
| 1096 | } | ||||
| 1097 | |||||
| 1098 | return parent; | ||||
| 1099 | } | ||||
| 1100 | |||||
| 1101 | /* merge one('from') node's children to another('to') node, and return the 'to' pointer */ | ||||
| 1102 | pbl_node_t* | ||||
| 1103 | // NOLINTNEXTLINE(misc-no-recursion) | ||||
| 1104 | pbl_merge_children(pbl_node_t* to, pbl_node_t* from) | ||||
| 1105 | { | ||||
| 1106 | GList* it; | ||||
| 1107 | pbl_node_t* child; | ||||
| 1108 | |||||
| 1109 | if (to == NULL((void*)0) || from == NULL((void*)0)) { | ||||
| 1110 | return to; | ||||
| 1111 | } | ||||
| 1112 | |||||
| 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); | ||||
| 1117 | } | ||||
| 1118 | |||||
| 1119 | g_queue_free(from->children); | ||||
| 1120 | from->children = NULL((void*)0); | ||||
| 1121 | if (from->children_by_name) { | ||||
| 1122 | g_hash_table_destroy(from->children_by_name); | ||||
| 1123 | } | ||||
| 1124 | from->children_by_name = NULL((void*)0); | ||||
| 1125 | |||||
| 1126 | if (from->nodetype == PBL_MESSAGE) { | ||||
| 1127 | pbl_message_descriptor_t* msg = (pbl_message_descriptor_t*) from; | ||||
| 1128 | if (msg->fields) { | ||||
| 1129 | g_queue_free(msg->fields); | ||||
| 1130 | msg->fields = NULL((void*)0); | ||||
| 1131 | } | ||||
| 1132 | if (msg->fields_by_number) { | ||||
| 1133 | g_hash_table_destroy(msg->fields_by_number); | ||||
| 1134 | msg->fields_by_number = NULL((void*)0); | ||||
| 1135 | } | ||||
| 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((void*)0); | ||||
| 1141 | } | ||||
| 1142 | if (anEnum->values_by_number) { | ||||
| 1143 | g_hash_table_destroy(anEnum->values_by_number); | ||||
| 1144 | anEnum->values_by_number = NULL((void*)0); | ||||
| 1145 | } | ||||
| 1146 | } | ||||
| 1147 | } | ||||
| 1148 | |||||
| 1149 | return to; | ||||
| 1150 | } | ||||
| 1151 | |||||
| 1152 | /* free a pbl_node_t and its children. */ | ||||
| 1153 | void | ||||
| 1154 | // NOLINTNEXTLINE(misc-no-recursion) | ||||
| 1155 | pbl_free_node(void *anode) | ||||
| 1156 | { | ||||
| 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; | ||||
| 1163 | |||||
| 1164 | if (node == NULL((void*)0)) return; | ||||
| 1165 | |||||
| 1166 | switch (node->nodetype) { | ||||
| 1167 | case PBL_METHOD: | ||||
| 1168 | method_node = (pbl_method_descriptor_t*) node; | ||||
| 1169 | g_free(method_node->in_msg_type); | ||||
| 1170 | g_free(method_node->out_msg_type); | ||||
| 1171 | break; | ||||
| 1172 | case PBL_MESSAGE: | ||||
| 1173 | message_node = (pbl_message_descriptor_t*) node; | ||||
| 1174 | if (message_node->fields) { | ||||
| 1175 | g_queue_free(message_node->fields); | ||||
| 1176 | } | ||||
| 1177 | if (message_node->fields_by_number) { | ||||
| 1178 | g_hash_table_destroy(message_node->fields_by_number); | ||||
| 1179 | } | ||||
| 1180 | break; | ||||
| 1181 | case PBL_FIELD: | ||||
| 1182 | case PBL_MAP_FIELD: | ||||
| 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); | ||||
| 1187 | } | ||||
| 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); | ||||
| 1191 | } | ||||
| 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); | ||||
| 1195 | } | ||||
| 1196 | break; | ||||
| 1197 | case PBL_ENUM: | ||||
| 1198 | enum_node = (pbl_enum_descriptor_t*) node; | ||||
| 1199 | if (enum_node->values) { | ||||
| 1200 | g_queue_free(enum_node->values); | ||||
| 1201 | } | ||||
| 1202 | if (enum_node->values_by_number) { | ||||
| 1203 | g_hash_table_destroy(enum_node->values_by_number); | ||||
| 1204 | } | ||||
| 1205 | break; | ||||
| 1206 | case PBL_OPTION: | ||||
| 1207 | option_node = (pbl_option_descriptor_t*) node; | ||||
| 1208 | g_free(option_node->value); | ||||
| 1209 | break; | ||||
| 1210 | default: | ||||
| 1211 | /* do nothing */ | ||||
| 1212 | break; | ||||
| 1213 | } | ||||
| 1214 | |||||
| 1215 | g_free(node->name); | ||||
| 1216 | g_free(node->full_name); | ||||
| 1217 | if (node->children) { | ||||
| 1218 | g_queue_free_full(node->children, pbl_free_node); | ||||
| 1219 | } | ||||
| 1220 | if (node->children_by_name) { | ||||
| 1221 | g_hash_table_destroy(node->children_by_name); | ||||
| 1222 | } | ||||
| 1223 | g_free(node); | ||||
| 1224 | } | ||||
| 1225 | |||||
| 1226 | /* | ||||
| 1227 | * Editor modelines - https://www.wireshark.org/tools/modelines.html | ||||
| 1228 | * | ||||
| 1229 | * Local variables: | ||||
| 1230 | * c-basic-offset: 4 | ||||
| 1231 | * tab-width: 8 | ||||
| 1232 | * indent-tabs-mode: nil | ||||
| 1233 | * End: | ||||
| 1234 | * | ||||
| 1235 | * vi: set shiftwidth=4 tabstop=8 expandtab: | ||||
| 1236 | * :indentSize=4:tabSize=8:noTabs=true: | ||||
| 1237 | */ |