Coverage Report

Created: 2026-05-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/protobuf_lang_tree.c
Line
Count
Source
1
/* protobuf_lang_tree.c
2
 *
3
 * Routines of building and reading Protocol Buffers Language grammar tree.
4
 * Copyright 2019, Huang Qiangxiong <qiangxiong.huang@qq.com>
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
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
0
#define MAX_PROTOBUF_NODE_DEPTH 100
22
static bool
23
check_node_depth(const pbl_node_t *node)
24
0
{
25
0
    int depth = 1;
26
0
    for (const pbl_node_t *parent = node; parent ; parent = parent->parent) {
27
0
        depth++;
28
0
    }
29
0
    if (depth > MAX_PROTOBUF_NODE_DEPTH) {
30
0
        return false;
31
0
    }
32
0
    return true;
33
0
}
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
0
{
53
0
    int src_len;
54
0
    uint8_t* dst, * q;
55
0
    const char* p = src;
56
57
0
    if (!(src && size && (src_len = (int)strlen(src))))
58
0
        return NULL;
59
60
0
    dst = q = (uint8_t *) g_malloc0(src_len + 1);
61
62
0
    while (p < src + src_len && *p) {
63
0
        if (*p == '\\') {
64
0
            p++;
65
66
0
            if (*p == 'x' || *p == 'X') { /* unescape hex byte */
67
0
                *q++ = (uint8_t)strtol(p + 1, (char**)&p, 16);
68
0
                continue;
69
0
            }
70
71
0
            if (*p >= '0' && *p <= '7') { /* unescape octal byte */
72
0
                *q++ = (uint8_t)strtol(p, (char**)&p, 8);
73
0
                continue;
74
0
            }
75
76
0
            switch (*p)
77
0
            {
78
0
            case 'a': *q++ = '\a'; break;
79
0
            case 'b': *q++ = '\b'; break;
80
0
            case 'f': *q++ = '\f'; break;
81
0
            case 'n': *q++ = '\n'; break;
82
0
            case 'r': *q++ = '\r'; break;
83
0
            case 't': *q++ = '\t'; break;
84
0
            case 'v': *q++ = '\v'; break;
85
0
            default: /* include copying '\', "'" or '"' */
86
0
                *q++ = *p;
87
0
                break;
88
0
            }
89
0
        } else {
90
0
            *q++ = *p;
91
0
        }
92
0
        p++;
93
0
    }
94
0
    *q = 0;
95
0
    *size = (int)(q - dst);
96
97
0
    return (char*) dst;
98
0
}
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
0
{
109
0
    unsigned i;
110
111
0
    pbl_free_pool(*ppool);
112
0
    pbl_descriptor_pool_t* p = g_new0(pbl_descriptor_pool_t, 1);
113
114
0
    p->source_paths = g_queue_new();
115
0
    for (i = 0; directories[i] != NULL; i++) {
116
0
        g_queue_push_tail(p->source_paths, g_strdup(directories[i]));
117
0
    }
118
119
0
    p->error_cb = error_cb ? error_cb : pbl_printf;
120
0
    p->packages = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, pbl_free_node);
121
0
    p->proto_files = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
122
0
    p->proto_files_to_be_parsed = g_queue_new();
123
124
0
    *ppool = p;
125
0
}
126
127
/* free all memory used by this protocol buffers language pool */
128
void
129
pbl_free_pool(pbl_descriptor_pool_t* pool)
130
0
{
131
0
    if (pool == NULL) return;
132
133
0
    g_queue_free_full(pool->source_paths, g_free);
134
0
    g_hash_table_destroy(pool->packages);
135
0
    g_queue_free(pool->proto_files_to_be_parsed); /* elements will be removed in p->proto_files */
136
0
    g_hash_table_destroy(pool->proto_files);
137
138
0
    g_free(pool);
139
0
}
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
0
{
153
0
    int i, j;
154
0
    char* canon_path = g_new(char, strlen(path) + 1);
155
    /* replace all '\' to '/', and change '//' to '/' */
156
0
    for (i = 0, j = 0; path[i] != '\0'; i++) {
157
0
        if (path[i] == '\\' || path[i] == '/') {
158
0
            if (j > 0 && canon_path[j-1] == '/') {
159
                /* ignore redundant slash */
160
0
            } else {
161
0
                canon_path[j++] = '/';
162
0
            }
163
0
        } else {
164
#ifdef _WIN32
165
            canon_path[j++] = g_ascii_tolower(path[i]);
166
#else
167
0
            canon_path[j++] = path[i];
168
0
#endif
169
0
        }
170
0
    }
171
0
    canon_path[j] = '\0';
172
173
0
    if (g_path_is_absolute(canon_path)
174
0
        && g_file_test(canon_path, G_FILE_TEST_IS_REGULAR)
175
0
        && strstr(canon_path, "/../") == NULL) {
176
0
        return canon_path;
177
0
    } else {
178
0
        g_free(canon_path);
179
0
        return NULL;
180
0
    }
181
0
}
182
183
/* Add a file into to do list */
184
bool
185
pbl_add_proto_file_to_be_parsed(pbl_descriptor_pool_t* pool, const char* filepath)
186
0
{
187
0
    char* path = NULL;
188
0
    GList* it = NULL;
189
0
    char* concat_path = NULL;
190
191
    /* Try to get the absolute path of the file */
192
0
    if (g_path_is_absolute(filepath)) {
193
0
        path = pbl_canonicalize_absolute_filepath(filepath);
194
0
    }
195
196
0
    if (path == NULL) {
197
        /* try to concat with source directories */
198
0
        for (it = g_queue_peek_head_link(pool->source_paths); it; it = it->next) {
199
0
            concat_path = g_build_filename((char*)it->data, filepath, NULL);
200
0
            path = pbl_canonicalize_absolute_filepath(concat_path);
201
0
            g_free(concat_path);
202
0
            if (path) break;
203
0
        }
204
0
    }
205
206
0
    if (path == NULL) {
207
0
        if (pool->parser_state) {
208
            /* only happened during parsing an 'import' line of a .proto file */
209
0
            pbl_parser_error(pool->parser_state, "file [%s] does not exist!\n", filepath);
210
0
        } else {
211
            /* normally happened during initializing a pool by adding files that need be loaded */
212
0
            pool->error_cb("Protobuf: file [%s] does not exist!\n", filepath);
213
0
        }
214
0
        return false;
215
0
    }
216
217
0
    if (!g_hash_table_lookup(pool->proto_files, path)) {
218
        /* create file descriptor info */
219
0
        pbl_file_descriptor_t* file = g_new0(pbl_file_descriptor_t, 1);
220
0
        file->filename = path;
221
0
        file->syntax_version = 2;
222
0
        file->package_name = PBL_DEFAULT_PACKAGE_NAME;
223
0
        file->package_name_lineno = -1;
224
0
        file->pool = pool;
225
226
        /* store in hash table and list */
227
0
        g_hash_table_insert(pool->proto_files, path, file);
228
0
        g_queue_push_tail(pool->proto_files_to_be_parsed, path);
229
0
    } else {
230
        /* The file is already in the proto_files */
231
0
        g_free(path);
232
0
    }
233
0
    return true;
234
0
}
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
0
{
240
0
    char* full_name_buf;
241
0
    int len, i;
242
0
    pbl_node_t* package;
243
0
    pbl_node_t* node = NULL;
244
0
    GSList* names = NULL; /* NULL terminated name retrieved from full_name */
245
0
    GSList* it = NULL;
246
247
0
    if (pool == NULL || full_name == NULL || pool->packages == NULL) {
248
0
        return NULL;
249
0
    }
250
251
0
    if (full_name[0] == '.') {
252
0
        full_name++; /* skip leading dot */
253
0
    }
254
255
0
    full_name_buf = g_strdup(full_name);
256
0
    len = (int)strlen(full_name_buf);
257
    /* scan from end to begin, and replace '.' to '\0' */
258
0
    for (i = len-1; i >= 0; i--) {
259
0
        if (full_name_buf[i] == '.' || i == 0) {
260
0
            if (i == 0) {
261
                /* no dot any more, we search in default package */
262
0
                names = g_slist_prepend(names, full_name_buf);
263
0
                package = (pbl_node_t*) g_hash_table_lookup(pool->packages, PBL_DEFAULT_PACKAGE_NAME);
264
0
            } else { /* replace middle dot with '\0' */
265
                /* push name at top of names */
266
0
                names = g_slist_prepend(names, full_name_buf + i + 1);
267
0
                full_name_buf[i] = 0;
268
                /* take 0~i of full_name_buf as package name */
269
0
                package = (pbl_node_t*) g_hash_table_lookup(pool->packages, full_name_buf);
270
0
            }
271
0
            if (package) {
272
0
                node = package;
273
                /* search node in this package */
274
0
                for (it = names; (it && node && node->children_by_name); it = it->next) {
275
0
                    node = (pbl_node_t*) g_hash_table_lookup(node->children_by_name, it->data);
276
0
                }
277
278
0
                if (it == NULL && node && node->nodetype == nodetype) {
279
0
                    break; /* found */
280
0
                }
281
0
                node = NULL;
282
0
            }
283
0
        }
284
0
    }
285
286
0
    if (names) {
287
0
        g_slist_free(names);
288
0
    }
289
0
    g_free(full_name_buf);
290
0
    return node;
291
0
}
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
0
{
298
0
    const char* parent_full_name;
299
0
    if (node == NULL
300
0
        || node->nodetype == PBL_UNKNOWN
301
0
        || node->nodetype == PBL_OPTIONS
302
0
        || node->nodetype == PBL_OPTION) {
303
0
        return NULL;
304
0
    }
305
306
0
    if (node->full_name) {
307
0
        return node->full_name;
308
0
    }
309
310
0
    if (node->nodetype == PBL_ONEOF) {
311
0
        if (!check_node_depth(node)) {
312
0
            return NULL;
313
0
        }
314
0
        return pbl_get_node_full_name(node->parent);
315
0
    }
316
317
0
    if (node->nodetype == PBL_PACKAGE) {
318
0
        node->full_name = g_strdup(node->name);
319
0
    } else {
320
0
        parent_full_name = pbl_get_node_full_name(node->parent);
321
0
        if (parent_full_name && parent_full_name[0] != 0) {
322
0
            node->full_name = g_strconcat(parent_full_name, ".", node->name, NULL);
323
0
        } else {
324
0
            node->full_name = g_strdup(node->name);
325
0
        }
326
0
    }
327
328
0
    return node->full_name;
329
0
}
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
0
{
335
0
    const pbl_node_t* node = NULL;
336
0
    pbl_descriptor_pool_t* pool = NULL;
337
0
    char* parent_name;
338
0
    char* full_name;
339
340
0
    if (context == NULL || name == NULL) {
341
0
        return NULL;
342
0
    }
343
344
0
    if (name[0] == '.') {
345
        /* A leading '.' (for example, .foo.bar.Baz) means to start from the outermost scope. */
346
0
        if (context->file && context->file->pool) {
347
0
            return pbl_find_node_in_pool(context->file->pool, name, nodetype);
348
0
        } else {
349
0
            return NULL;
350
0
        }
351
0
    }
352
353
    /* find pool */
354
0
    if (context->file) {
355
0
        pool = context->file->pool;
356
0
    }
357
358
    /* try find node in the context or parents (message or package) of the context */
359
0
    if (pool) {
360
0
        int remaining;
361
0
        parent_name = g_strdup(pbl_get_node_full_name((pbl_node_t*) context));
362
0
        remaining = (int)strlen(parent_name);
363
0
        while (remaining > 0) {
364
0
            full_name = g_strconcat(parent_name, ".", name, NULL);
365
0
            node = pbl_find_node_in_pool(pool, full_name, nodetype);
366
0
            g_free(full_name);
367
0
            if (node) {
368
0
                g_free(parent_name);
369
0
                return node;
370
0
            }
371
            /* scan from end to begin, and replace first '.' to '\0' */
372
0
            for (remaining--; remaining > 0; remaining--) {
373
0
                if (parent_name[remaining] == '.') {
374
                    /* found a potential parent node name */
375
0
                    parent_name[remaining] = '\0';
376
0
                    break; /* break from the 'for' loop, continue 'while' loop */
377
0
                }
378
0
            }
379
0
        }
380
0
        g_free(parent_name);
381
382
        /* try find node in pool directly */
383
0
        return pbl_find_node_in_pool(pool, name, nodetype);
384
0
    }
385
386
0
    return NULL;
387
0
}
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
0
{
393
0
    pbl_node_t* n = pbl_find_node_in_pool(pool, full_name, PBL_METHOD);
394
0
    return n ? (pbl_method_descriptor_t*)n : NULL;
395
0
}
396
397
/* like MethodDescriptor::name() */
398
const char*
399
pbl_method_descriptor_name(const pbl_method_descriptor_t* method)
400
0
{
401
0
    return pbl_get_node_name((pbl_node_t*)method);
402
0
}
403
404
/* like MethodDescriptor::full_name() */
405
const char*
406
pbl_method_descriptor_full_name(const pbl_method_descriptor_t* method)
407
0
{
408
0
    return pbl_get_node_full_name((pbl_node_t*)method);
409
0
}
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
0
{
415
0
    const pbl_node_t* n = pbl_find_node_in_context((pbl_node_t*)method, method->in_msg_type, PBL_MESSAGE);
416
0
    return n ? (const pbl_message_descriptor_t*)n : NULL;
417
0
}
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
0
{
423
0
    const pbl_node_t* n = pbl_find_node_in_context((pbl_node_t*)method, method->out_msg_type, PBL_MESSAGE);
424
0
    return n ? (const pbl_message_descriptor_t*)n : NULL;
425
0
}
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
0
{
431
0
    pbl_node_t* n = pbl_find_node_in_pool(pool, name, PBL_MESSAGE);
432
0
    return n ? (pbl_message_descriptor_t*)n : NULL;
433
0
}
434
435
/* like Descriptor::name() */
436
const char*
437
pbl_message_descriptor_name(const pbl_message_descriptor_t* message)
438
0
{
439
0
    return pbl_get_node_name((pbl_node_t*)message);
440
0
}
441
442
/* like Descriptor::full_name() */
443
const char*
444
pbl_message_descriptor_full_name(const pbl_message_descriptor_t* message)
445
0
{
446
0
    return pbl_get_node_full_name((pbl_node_t*)message);
447
0
}
448
449
/* like Descriptor::field_count() */
450
int
451
pbl_message_descriptor_field_count(const pbl_message_descriptor_t* message)
452
0
{
453
0
    return (message && message->fields) ? g_queue_get_length(message->fields) : 0;
454
0
}
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
0
{
460
0
    return (message && message->fields) ? (pbl_field_descriptor_t*) g_queue_peek_nth(message->fields, field_index) : NULL;
461
0
}
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
0
{
467
0
    if (message && message->fields_by_number) {
468
0
        return (pbl_field_descriptor_t*) g_hash_table_lookup(message->fields_by_number, GINT_TO_POINTER(number));
469
0
    } else {
470
0
        return NULL;
471
0
    }
472
0
}
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
0
{
478
0
    if (message && ((pbl_node_t*)message)->children_by_name) {
479
0
        return (pbl_field_descriptor_t*) g_hash_table_lookup(((pbl_node_t*)message)->children_by_name, name);
480
0
    } else {
481
0
        return NULL;
482
0
    }
483
0
}
484
485
/* like FieldDescriptor::full_name() */
486
const char*
487
pbl_field_descriptor_full_name(const pbl_field_descriptor_t* field)
488
0
{
489
0
    return pbl_get_node_full_name((pbl_node_t*)field);
490
0
}
491
492
/* like FieldDescriptor::name() */
493
const char*
494
pbl_field_descriptor_name(const pbl_field_descriptor_t* field)
495
0
{
496
0
    return pbl_get_node_name((pbl_node_t*)field);
497
0
}
498
499
/* like FieldDescriptor::number() */
500
int
501
pbl_field_descriptor_number(const pbl_field_descriptor_t* field)
502
0
{
503
0
    return GPOINTER_TO_INT(field->number);
504
0
}
505
506
/* like FieldDescriptor::type() */
507
int
508
pbl_field_descriptor_type(const pbl_field_descriptor_t* field)
509
0
{
510
0
    const pbl_node_t* node;
511
0
    if (field->type == PROTOBUF_TYPE_NONE) {
512
        /* try to lookup as ENUM */
513
0
        node = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_ENUM);
514
0
        if (node) {
515
0
            ((pbl_field_descriptor_t*)field)->type = PROTOBUF_TYPE_ENUM;
516
0
        } else {
517
            /* try to lookup as MESSAGE */
518
0
            node = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_MESSAGE);
519
0
            if (node) {
520
0
                ((pbl_field_descriptor_t*)field)->type = PROTOBUF_TYPE_MESSAGE;
521
0
            }
522
0
        }
523
0
    }
524
0
    return field->type;
525
0
}
526
527
/* like FieldDescriptor::is_repeated() */
528
int
529
pbl_field_descriptor_is_repeated(const pbl_field_descriptor_t* field)
530
0
{
531
0
    return field->is_repeated ? 1 : 0;
532
0
}
533
534
/* like FieldDescriptor::is_packed() */
535
int
536
pbl_field_descriptor_is_packed(const pbl_field_descriptor_t* field)
537
0
{
538
0
    bool has_packed_option;
539
0
    bool packed_option_value;
540
0
    int syntax_version = ((pbl_node_t*)field)->file->syntax_version;
541
542
    /* determine packed flag */
543
0
    if (field->is_repeated == false) {
544
0
        return false;
545
0
    }
546
    /* note: field->type may be undetermined until calling pbl_field_descriptor_type() */
547
0
    switch (pbl_field_descriptor_type(field)) {
548
0
    case PROTOBUF_TYPE_STRING:
549
0
    case PROTOBUF_TYPE_GROUP:
550
0
    case PROTOBUF_TYPE_MESSAGE:
551
0
    case PROTOBUF_TYPE_BYTES:
552
0
        return false;
553
0
    default: /* only repeated fields of primitive numeric types can be declared "packed". */
554
0
        has_packed_option = field->options_node
555
0
            && field->options_node->children_by_name
556
0
            && g_hash_table_lookup(field->options_node->children_by_name, "packed");
557
558
0
        packed_option_value = (has_packed_option ?
559
0
            g_strcmp0(
560
0
              ((pbl_option_descriptor_t*)g_hash_table_lookup(
561
0
                field->options_node->children_by_name, "packed"))->value, "true") == 0
562
0
            : false);
563
564
0
        if (syntax_version == 2) {
565
0
            return packed_option_value;
566
0
        } else { /* packed default in syntax_version = 3 */
567
0
            return has_packed_option ? packed_option_value : true;
568
0
        }
569
0
    }
570
0
}
571
572
/* like FieldDescriptor::TypeName() */
573
const char*
574
pbl_field_descriptor_TypeName(wmem_allocator_t* scope, int field_type)
575
0
{
576
0
    return val_to_str(scope, field_type, protobuf_field_type, "UNKNOWN_FIELD_TYPE(%d)");
577
0
}
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
0
{
583
0
    const pbl_node_t* n;
584
0
    if (field->type == PROTOBUF_TYPE_MESSAGE || field->type == PROTOBUF_TYPE_GROUP) {
585
0
        n = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_MESSAGE);
586
0
        return n ? (const pbl_message_descriptor_t*)n : NULL;
587
0
    }
588
0
    return NULL;
589
0
}
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
0
{
595
0
    const pbl_node_t* n;
596
0
    if (field->type == PROTOBUF_TYPE_ENUM) {
597
0
        n = pbl_find_node_in_context(((pbl_node_t*)field)->parent, field->type_name, PBL_ENUM);
598
0
        return n ? (const pbl_enum_descriptor_t*)n : NULL;
599
0
    }
600
0
    return NULL;
601
0
}
602
603
/* like FieldDescriptor::is_required() */
604
bool
605
pbl_field_descriptor_is_required(const pbl_field_descriptor_t* field)
606
0
{
607
0
    return field->is_required;
608
0
}
609
610
/* like FieldDescriptor::has_default_value() */
611
bool
612
pbl_field_descriptor_has_default_value(const pbl_field_descriptor_t* field)
613
0
{
614
0
    return field->has_default_value;
615
0
}
616
617
/* like FieldDescriptor::default_value_int32() */
618
int32_t
619
pbl_field_descriptor_default_value_int32(const pbl_field_descriptor_t* field)
620
0
{
621
0
    return field->default_value.i32;
622
0
}
623
624
/* like FieldDescriptor::default_value_int64() */
625
int64_t
626
pbl_field_descriptor_default_value_int64(const pbl_field_descriptor_t* field)
627
0
{
628
0
    return field->default_value.i64;
629
0
}
630
631
/* like FieldDescriptor::default_value_uint32() */
632
uint32_t
633
pbl_field_descriptor_default_value_uint32(const pbl_field_descriptor_t* field)
634
0
{
635
0
    return field->default_value.u32;
636
0
}
637
638
/* like FieldDescriptor::default_value_uint64() */
639
uint64_t
640
pbl_field_descriptor_default_value_uint64(const pbl_field_descriptor_t* field)
641
0
{
642
0
    return field->default_value.u64;
643
0
}
644
645
/* like FieldDescriptor::default_value_float() */
646
float
647
pbl_field_descriptor_default_value_float(const pbl_field_descriptor_t* field)
648
0
{
649
0
    return field->default_value.f;
650
0
}
651
652
/* like FieldDescriptor::default_value_double() */
653
double
654
pbl_field_descriptor_default_value_double(const pbl_field_descriptor_t* field)
655
0
{
656
0
    return field->default_value.d;
657
0
}
658
659
/* like FieldDescriptor::default_value_bool() */
660
bool
661
pbl_field_descriptor_default_value_bool(const pbl_field_descriptor_t* field)
662
0
{
663
0
    return field->default_value.b;
664
0
}
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
0
{
670
0
    *size = field->string_or_bytes_default_value_length;
671
0
    return field->default_value.s;
672
0
}
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
0
{
678
0
    const pbl_enum_descriptor_t* enum_desc;
679
680
0
    if (pbl_field_descriptor_type(field) == PROTOBUF_TYPE_ENUM
681
0
        && field->default_value.e == NULL && (enum_desc = pbl_field_descriptor_enum_type(field))) {
682
683
0
        if (field->orig_default_value) {
684
            /* find enum_value according to the name of default value */
685
0
            ((pbl_field_descriptor_t*)field)->default_value.e = pbl_enum_descriptor_FindValueByName(enum_desc, field->orig_default_value);
686
0
        } else {
687
            /* the default value is the first defined enum value */
688
0
            ((pbl_field_descriptor_t*)field)->default_value.e = pbl_enum_descriptor_value(enum_desc, 0);
689
0
        }
690
0
    }
691
692
0
    return field->default_value.e;
693
0
}
694
695
/* like EnumDescriptor::name() */
696
const char*
697
pbl_enum_descriptor_name(const pbl_enum_descriptor_t* anEnum)
698
0
{
699
0
    return pbl_get_node_name((pbl_node_t*)anEnum);
700
0
}
701
702
/* like EnumDescriptor::full_name() */
703
const char*
704
pbl_enum_descriptor_full_name(const pbl_enum_descriptor_t* anEnum)
705
0
{
706
0
    return pbl_get_node_full_name((pbl_node_t*)anEnum);
707
0
}
708
709
/* like EnumDescriptor::value_count() */
710
int
711
pbl_enum_descriptor_value_count(const pbl_enum_descriptor_t* anEnum)
712
0
{
713
0
    return (anEnum && anEnum->values) ? g_queue_get_length(anEnum->values) : 0;
714
0
}
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
0
{
720
0
    return (anEnum && anEnum->values) ? (pbl_enum_value_descriptor_t*) g_queue_peek_nth(anEnum->values, value_index) : NULL;
721
0
}
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
0
{
727
0
    if (anEnum && anEnum->values_by_number) {
728
0
        return (pbl_enum_value_descriptor_t*) g_hash_table_lookup(anEnum->values_by_number, GINT_TO_POINTER(number));
729
0
    } else {
730
0
        return NULL;
731
0
    }
732
0
}
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
0
{
738
0
    if (anEnum && ((pbl_node_t*)anEnum)->children_by_name) {
739
0
        return (pbl_enum_value_descriptor_t*)g_hash_table_lookup(((pbl_node_t*)anEnum)->children_by_name, name);
740
0
    } else {
741
0
        return NULL;
742
0
    }
743
0
}
744
745
/* like EnumValueDescriptor::name() */
746
const char*
747
pbl_enum_value_descriptor_name(const pbl_enum_value_descriptor_t* enumValue)
748
0
{
749
0
    return pbl_get_node_name((pbl_node_t*)enumValue);
750
0
}
751
752
/* like EnumValueDescriptor::full_name() */
753
const char*
754
pbl_enum_value_descriptor_full_name(const pbl_enum_value_descriptor_t* enumValue)
755
0
{
756
0
    return pbl_get_node_full_name((pbl_node_t*)enumValue);
757
0
}
758
759
/* like EnumValueDescriptor::number() */
760
int
761
pbl_enum_value_descriptor_number(const pbl_enum_value_descriptor_t* enumValue)
762
0
{
763
0
    return GPOINTER_TO_INT(enumValue->number);
764
0
}
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
0
{
770
0
    GList* it;
771
0
    if (node == NULL) {
772
0
        return;
773
0
    }
774
775
0
    if (node->nodetype == PBL_MESSAGE) {
776
0
        (*cb)((const pbl_message_descriptor_t*) node, userdata);
777
0
    }
778
779
0
    if (node->children) {
780
0
        if (!check_node_depth(node)) {
781
0
            return;
782
0
        }
783
0
        for (it = g_queue_peek_head_link(node->children); it; it = it->next) {
784
0
            pbl_traverse_sub_tree((const pbl_node_t*) it->data, cb, userdata);
785
0
        }
786
0
    }
787
0
}
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
0
{
793
0
    GHashTableIter it;
794
0
    void *key, *value;
795
0
    g_hash_table_iter_init (&it, pool->packages);
796
0
    while (g_hash_table_iter_next (&it, &key, &value)) {
797
0
        pbl_traverse_sub_tree((const pbl_node_t*)value, cb, userdata);
798
0
    }
799
0
}
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
0
{
809
0
    node->nodetype = nodetype;
810
0
    node->name = g_strdup(name);
811
0
    node->file = file;
812
0
    node->lineno = (lineno > -1) ? lineno : -1;
813
0
}
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
0
{
819
0
    pbl_node_t* node = NULL;
820
821
0
    switch (nodetype) {
822
0
    case PBL_METHOD:     /* should use pbl_create_method_node() */
823
0
    case PBL_FIELD:      /* should use pbl_create_field_node() */
824
0
    case PBL_MAP_FIELD:  /* should use pbl_create_map_field_node() */
825
0
    case PBL_ENUM_VALUE: /* should use pbl_create_enum_value_node() */
826
0
    case PBL_OPTION:     /* should use pbl_create_option_node() */
827
0
        return NULL;
828
0
    case PBL_MESSAGE:
829
0
        node = (pbl_node_t*) g_malloc0(sizeof(pbl_message_descriptor_t));
830
0
        break;
831
0
    case PBL_ENUM:
832
0
        node = (pbl_node_t*) g_malloc0(sizeof(pbl_enum_descriptor_t));
833
0
        break;
834
0
    default:
835
0
        node = g_new0(pbl_node_t, 1);
836
0
    }
837
0
    pbl_init_node(node, file, lineno, nodetype, name);
838
0
    return node;
839
0
}
840
841
pbl_node_t*
842
pbl_set_node_name(pbl_node_t* node, int lineno, const char* newname)
843
0
{
844
0
    g_free(node->name);
845
0
    node->name = g_strdup(newname);
846
0
    if (lineno > -1) {
847
0
        node->lineno = lineno;
848
0
    }
849
0
    return node;
850
0
}
851
852
static pbl_option_descriptor_t*
853
pbl_get_option_by_name(pbl_node_t* options, const char* name)
854
0
{
855
0
    if (options && options->children_by_name) {
856
0
        return (pbl_option_descriptor_t*)g_hash_table_lookup(options->children_by_name, name);
857
0
    } else {
858
0
        return NULL;
859
0
    }
860
0
}
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 in_is_stream, const char* out_msg_type, bool out_is_stream)
866
0
{
867
0
    pbl_method_descriptor_t* node = g_new0(pbl_method_descriptor_t, 1);
868
0
    pbl_init_node(&node->basic_info, file, lineno, PBL_METHOD, name);
869
870
0
    node->in_msg_type = g_strdup(in_msg_type);
871
0
    node->in_is_stream = in_is_stream;
872
0
    node->out_msg_type = g_strdup(out_msg_type);
873
0
    node->out_is_stream = out_is_stream;
874
875
0
    return (pbl_node_t*)node;
876
0
}
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
0
{
883
0
    int i = str_to_val(type_name, protobuf_field_type, 0);
884
0
    if (i == PROTOBUF_TYPE_GROUP || i == PROTOBUF_TYPE_MESSAGE || i == PROTOBUF_TYPE_ENUM) {
885
0
        i = PROTOBUF_TYPE_NONE; /* complex type will find after parsing */
886
0
    }
887
888
0
    return i;
889
0
}
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
0
{
895
0
    pbl_option_descriptor_t* default_option;
896
0
    pbl_field_descriptor_t* node = g_new0(pbl_field_descriptor_t, 1);
897
0
    pbl_init_node(&node->basic_info, file, lineno, PBL_FIELD, name);
898
899
0
    node->number = number;
900
0
    node->options_node = options;
901
0
    node->is_repeated = (g_strcmp0(label, "repeated") == 0);
902
0
    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
0
    node->type = pbl_get_simple_type_enum_value_by_typename(type_name);
906
0
    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
0
    default_option = pbl_get_option_by_name(options, "default");
913
0
    if (default_option && default_option->value) {
914
0
        node->has_default_value = true;
915
0
        node->orig_default_value = g_strdup(default_option->value);
916
        /* get default value for simple type */
917
0
        switch (node->type)
918
0
        {
919
0
        case PROTOBUF_TYPE_INT32:
920
0
        case PROTOBUF_TYPE_SINT32:
921
0
        case PROTOBUF_TYPE_SFIXED32:
922
0
            sscanf(node->orig_default_value, "%" SCNd32, &node->default_value.i32);
923
0
            break;
924
925
0
        case PROTOBUF_TYPE_INT64:
926
0
        case PROTOBUF_TYPE_SINT64:
927
0
        case PROTOBUF_TYPE_SFIXED64:
928
0
            node->default_value.i64 = g_ascii_strtoll(node->orig_default_value, NULL, 10);
929
0
            break;
930
931
0
        case PROTOBUF_TYPE_UINT32:
932
0
        case PROTOBUF_TYPE_FIXED32:
933
0
            sscanf(node->orig_default_value, "%" SCNu32, &node->default_value.u32);
934
0
            break;
935
936
0
        case PROTOBUF_TYPE_UINT64:
937
0
        case PROTOBUF_TYPE_FIXED64:
938
0
            node->default_value.u64 = g_ascii_strtoull(node->orig_default_value, NULL, 10);
939
0
            break;
940
941
0
        case PROTOBUF_TYPE_BOOL:
942
0
            node->default_value.b = (g_strcmp0(node->orig_default_value, "true") == 0);
943
0
            break;
944
945
0
        case PROTOBUF_TYPE_DOUBLE:
946
0
            node->default_value.d = g_ascii_strtod(node->orig_default_value, NULL);
947
0
            break;
948
949
0
        case PROTOBUF_TYPE_FLOAT:
950
0
            node->default_value.f = (float) g_ascii_strtod(node->orig_default_value, NULL);
951
0
            break;
952
953
0
        case PROTOBUF_TYPE_STRING:
954
0
        case PROTOBUF_TYPE_BYTES:
955
0
            node->default_value.s = protobuf_string_unescape(node->orig_default_value, &node->string_or_bytes_default_value_length);
956
0
            break;
957
958
0
        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
0
            break;
964
0
        }
965
0
    }
966
967
0
    return (pbl_node_t*)node;
968
0
}
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
0
{
974
0
    pbl_field_descriptor_t* node = g_new0(pbl_field_descriptor_t, 1);
975
0
    pbl_init_node(&node->basic_info, file, lineno, PBL_MAP_FIELD, name);
976
977
0
    node->number = number;
978
0
    node->type_name = g_strconcat(name, "MapEntry", NULL);
979
0
    node->type = PROTOBUF_TYPE_MESSAGE;
980
0
    node->is_repeated = true;
981
0
    node->options_node = options;
982
983
0
    return (pbl_node_t*)node;
984
0
}
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
0
{
990
0
    pbl_enum_value_descriptor_t* node = g_new0(pbl_enum_value_descriptor_t, 1);
991
0
    pbl_init_node(&node->basic_info, file, lineno, PBL_ENUM_VALUE, name);
992
993
0
    node->number = number;
994
0
    return (pbl_node_t*)node;
995
0
}
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
0
{
1001
0
    pbl_option_descriptor_t* node = g_new0(pbl_option_descriptor_t, 1);
1002
0
    pbl_init_node(&node->basic_info, file, lineno, PBL_OPTION, name);
1003
1004
0
    if (value)
1005
0
        node->value = g_strdup(value);
1006
0
    return (pbl_node_t*)node;
1007
0
}
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
0
{
1014
0
    pbl_node_t* node = NULL;
1015
0
    if (child == NULL || parent == NULL) {
1016
0
        return parent;
1017
0
    }
1018
1019
0
    if (!check_node_depth(parent)) {
1020
0
        return NULL;
1021
0
    }
1022
1023
    /* add a message node for mapField first */
1024
0
    if (child->nodetype == PBL_MAP_FIELD) {
1025
0
        node = pbl_create_node(child->file, child->lineno, PBL_MESSAGE, ((pbl_field_descriptor_t*)child)->type_name);
1026
0
        pbl_merge_children(node, child);
1027
0
        pbl_add_child(parent, node);
1028
0
    }
1029
1030
0
    child->parent = parent;
1031
1032
    /* add child to children list */
1033
0
    if (parent->children == NULL) {
1034
0
        parent->children = g_queue_new();
1035
0
    }
1036
0
    g_queue_push_tail(parent->children, child);
1037
1038
    /* add child to children_by_name table */
1039
0
    if (parent->children_by_name == NULL) {
1040
0
        parent->children_by_name = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
1041
0
    }
1042
1043
0
    node = (pbl_node_t*) g_hash_table_lookup(parent->children_by_name, child->name);
1044
0
    if (node && node->nodetype == PBL_OPTION && child->nodetype == PBL_OPTION
1045
0
        && ((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
0
        char* oval = ((pbl_option_descriptor_t*)node)->value;
1051
0
        char* nval = ((pbl_option_descriptor_t*)child)->value;
1052
0
        ((pbl_option_descriptor_t*)child)->value = g_strconcat(oval, ",", nval, NULL);
1053
0
        g_free(nval);
1054
0
    } else if (node && child->file && parent->file
1055
0
    && child->file->pool && child->file->pool->error_cb
1056
    /* Let's assume that any set of base types we point at are valid.. */
1057
0
    && !strstr(node->file->filename, "google")) {
1058
0
        child->file->pool->error_cb(
1059
0
            "Protobuf: Warning: \"%s\" of [%s:%d] is already defined in file [%s:%d].\n",
1060
0
            child->name, child->file->filename, child->lineno, node->file->filename, node->lineno);
1061
0
    }
1062
1063
0
    g_hash_table_insert(parent->children_by_name, child->name, child);
1064
1065
0
    if (parent->nodetype == PBL_MESSAGE) {
1066
0
        pbl_message_descriptor_t* msg = (pbl_message_descriptor_t*) parent;
1067
1068
        /* add child to fields_by_number table */
1069
0
        if (child->nodetype == PBL_FIELD || child->nodetype == PBL_MAP_FIELD) {
1070
0
            if (msg->fields == NULL) {
1071
0
                msg->fields = g_queue_new();
1072
0
            }
1073
0
            g_queue_push_tail(msg->fields, child);
1074
1075
0
            if (msg->fields_by_number == NULL) {
1076
0
                msg->fields_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
1077
0
            }
1078
0
            g_hash_table_insert(msg->fields_by_number,
1079
0
                                GINT_TO_POINTER(((pbl_field_descriptor_t*)child)->number), child);
1080
0
        }
1081
1082
0
    } else if (parent->nodetype == PBL_ENUM && child->nodetype == PBL_ENUM_VALUE) {
1083
0
        pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) parent;
1084
1085
0
        if (anEnum->values == NULL) {
1086
0
            anEnum->values = g_queue_new();
1087
0
        }
1088
0
        g_queue_push_tail(anEnum->values, child);
1089
1090
        /* add child to values_by_number table */
1091
0
        if (anEnum->values_by_number == NULL) {
1092
0
            anEnum->values_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
1093
0
        }
1094
0
        g_hash_table_insert(anEnum->values_by_number,
1095
0
                            GINT_TO_POINTER(((pbl_enum_value_descriptor_t*)child)->number), child);
1096
0
    }
1097
1098
0
    return parent;
1099
0
}
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
0
{
1106
0
    GList* it;
1107
0
    pbl_node_t* child;
1108
1109
0
    if (to == NULL || from == NULL) {
1110
0
        return to;
1111
0
    }
1112
1113
0
    if (from->children) {
1114
0
        for (it = g_queue_peek_head_link(from->children); it; it = it->next) {
1115
0
            child = (pbl_node_t*)it->data;
1116
0
            pbl_add_child(to, child);
1117
0
        }
1118
1119
0
        g_queue_free(from->children);
1120
0
        from->children = NULL;
1121
0
        if (from->children_by_name) {
1122
0
            g_hash_table_destroy(from->children_by_name);
1123
0
        }
1124
0
        from->children_by_name = NULL;
1125
1126
0
        if (from->nodetype == PBL_MESSAGE) {
1127
0
            pbl_message_descriptor_t* msg = (pbl_message_descriptor_t*) from;
1128
0
            if (msg->fields) {
1129
0
                g_queue_free(msg->fields);
1130
0
                msg->fields = NULL;
1131
0
            }
1132
0
            if (msg->fields_by_number) {
1133
0
                g_hash_table_destroy(msg->fields_by_number);
1134
0
                msg->fields_by_number = NULL;
1135
0
            }
1136
0
        } else if (from->nodetype == PBL_ENUM) {
1137
0
            pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) from;
1138
0
            if (anEnum->values) {
1139
0
                g_queue_free(anEnum->values);
1140
0
                anEnum->values = NULL;
1141
0
            }
1142
0
            if (anEnum->values_by_number) {
1143
0
                g_hash_table_destroy(anEnum->values_by_number);
1144
0
                anEnum->values_by_number = NULL;
1145
0
            }
1146
0
        }
1147
0
    }
1148
1149
0
    return to;
1150
0
}
1151
1152
/* free a pbl_node_t and its children. */
1153
void
1154
// NOLINTNEXTLINE(misc-no-recursion)
1155
pbl_free_node(void *anode)
1156
0
{
1157
0
    pbl_method_descriptor_t* method_node;
1158
0
    pbl_message_descriptor_t* message_node;
1159
0
    pbl_field_descriptor_t* field_node;
1160
0
    pbl_enum_descriptor_t* enum_node;
1161
0
    pbl_option_descriptor_t* option_node;
1162
0
    pbl_node_t* node = (pbl_node_t*) anode;
1163
1164
0
    if (node == NULL) return;
1165
1166
0
    switch (node->nodetype) {
1167
0
    case PBL_METHOD:
1168
0
        method_node = (pbl_method_descriptor_t*) node;
1169
0
        g_free(method_node->in_msg_type);
1170
0
        g_free(method_node->out_msg_type);
1171
0
        break;
1172
0
    case PBL_MESSAGE:
1173
0
        message_node = (pbl_message_descriptor_t*) node;
1174
0
        if (message_node->fields) {
1175
0
            g_queue_free(message_node->fields);
1176
0
        }
1177
0
        if (message_node->fields_by_number) {
1178
0
            g_hash_table_destroy(message_node->fields_by_number);
1179
0
        }
1180
0
        break;
1181
0
    case PBL_FIELD:
1182
0
    case PBL_MAP_FIELD:
1183
0
        field_node = (pbl_field_descriptor_t*) node;
1184
0
        g_free(field_node->type_name);
1185
0
        if (field_node->orig_default_value) {
1186
0
            g_free(field_node->orig_default_value);
1187
0
        }
1188
0
        if ((field_node->type == PROTOBUF_TYPE_STRING || field_node->type == PROTOBUF_TYPE_BYTES)
1189
0
            && field_node->default_value.s) {
1190
0
            g_free(field_node->default_value.s);
1191
0
        }
1192
0
        if (field_node->options_node) {
1193
            // We recurse here, but we're limited by depth checks at allocation time
1194
0
            pbl_free_node(field_node->options_node);
1195
0
        }
1196
0
        break;
1197
0
    case PBL_ENUM:
1198
0
        enum_node = (pbl_enum_descriptor_t*) node;
1199
0
        if (enum_node->values) {
1200
0
            g_queue_free(enum_node->values);
1201
0
        }
1202
0
        if (enum_node->values_by_number) {
1203
0
            g_hash_table_destroy(enum_node->values_by_number);
1204
0
        }
1205
0
        break;
1206
0
    case PBL_OPTION:
1207
0
        option_node = (pbl_option_descriptor_t*) node;
1208
0
        g_free(option_node->value);
1209
0
        break;
1210
0
    default:
1211
        /* do nothing */
1212
0
        break;
1213
0
    }
1214
1215
0
    g_free(node->name);
1216
0
    g_free(node->full_name);
1217
0
    if (node->children) {
1218
0
        g_queue_free_full(node->children, pbl_free_node);
1219
0
    }
1220
0
    if (node->children_by_name) {
1221
0
        g_hash_table_destroy(node->children_by_name);
1222
0
    }
1223
0
    g_free(node);
1224
0
}
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
 */