| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2023 Google LLC. All rights reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file or at | |
| // https://developers.google.com/open-source/licenses/bsd | |
| #include "upb/util/required_fields.h" | |
| #include <assert.h> | |
| #include <inttypes.h> | |
| #include <setjmp.h> | |
| #include <stdarg.h> | |
| #include <stdbool.h> | |
| #include <stddef.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "upb/base/descriptor_constants.h" | |
| #include "upb/base/status.h" | |
| #include "upb/mem/alloc.h" | |
| #include "upb/message/array.h" | |
| #include "upb/message/map.h" | |
| #include "upb/message/message.h" | |
| #include "upb/port/vsnprintf_compat.h" | |
| #include "upb/reflection/def.h" | |
| #include "upb/reflection/message.h" | |
| // Must be last. | |
| #include "upb/port/def.inc" | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // upb_FieldPath_ToText() | |
| //////////////////////////////////////////////////////////////////////////////// | |
| typedef struct { | |
| char* buf; | |
| char* ptr; | |
| char* end; | |
| size_t overflow; | |
| } upb_PrintfAppender; | |
| UPB_PRINTF(2, 3) | |
| static void upb_FieldPath_Printf(upb_PrintfAppender* a, const char* fmt, ...) { | |
| size_t n; | |
| size_t have = a->end - a->ptr; | |
| va_list args; | |
| va_start(args, fmt); | |
| n = _upb_vsnprintf(a->ptr, have, fmt, args); | |
| va_end(args); | |
| if (UPB_LIKELY(have > n)) { | |
| // We can't end up here if the user passed (NULL, 0), therefore ptr is known | |
| // to be non-NULL, and UPB_PTRADD() is not necessary. | |
| assert(a->ptr); | |
| a->ptr += n; | |
| } else { | |
| a->ptr = UPB_PTRADD(a->ptr, have); | |
| a->overflow += (n - have); | |
| } | |
| } | |
| static size_t upb_FieldPath_NullTerminate(upb_PrintfAppender* d, size_t size) { | |
| size_t ret = d->ptr - d->buf + d->overflow; | |
| if (size > 0) { | |
| if (d->ptr == d->end) d->ptr--; | |
| *d->ptr = '\0'; | |
| } | |
| return ret; | |
| } | |
| static void upb_FieldPath_PutMapKey(upb_PrintfAppender* a, | |
| upb_MessageValue map_key, | |
| const upb_FieldDef* key_f) { | |
| switch (upb_FieldDef_CType(key_f)) { | |
| case kUpb_CType_Int32: | |
| upb_FieldPath_Printf(a, "[%" PRId32 "]", map_key.int32_val); | |
| break; | |
| case kUpb_CType_Int64: | |
| upb_FieldPath_Printf(a, "[%" PRId64 "]", map_key.int64_val); | |
| break; | |
| case kUpb_CType_UInt32: | |
| upb_FieldPath_Printf(a, "[%" PRIu32 "]", map_key.uint32_val); | |
| break; | |
| case kUpb_CType_UInt64: | |
| upb_FieldPath_Printf(a, "[%" PRIu64 "]", map_key.uint64_val); | |
| break; | |
| case kUpb_CType_Bool: | |
| upb_FieldPath_Printf(a, "[%s]", map_key.bool_val ? "true" : "false"); | |
| break; | |
| case kUpb_CType_String: | |
| upb_FieldPath_Printf(a, "[\""); | |
| for (size_t i = 0; i < map_key.str_val.size; i++) { | |
| char ch = map_key.str_val.data[i]; | |
| if (ch == '"') { | |
| upb_FieldPath_Printf(a, "\\\""); | |
| } else { | |
| upb_FieldPath_Printf(a, "%c", ch); | |
| } | |
| } | |
| upb_FieldPath_Printf(a, "\"]"); | |
| break; | |
| default: | |
| UPB_UNREACHABLE(); // Other types can't be map keys. | |
| } | |
| } | |
| size_t upb_FieldPath_ToText(upb_FieldPathEntry** path, char* buf, size_t size) { | |
| upb_FieldPathEntry* ptr = *path; | |
| upb_PrintfAppender appender; | |
| appender.buf = buf; | |
| appender.ptr = buf; | |
| appender.end = UPB_PTRADD(buf, size); | |
| appender.overflow = 0; | |
| bool first = true; | |
| while (ptr->field) { | |
| const upb_FieldDef* f = ptr->field; | |
| upb_FieldPath_Printf(&appender, first ? "%s" : ".%s", upb_FieldDef_Name(f)); | |
| first = false; | |
| ptr++; | |
| if (upb_FieldDef_IsMap(f)) { | |
| const upb_FieldDef* key_f = | |
| upb_MessageDef_Field(upb_FieldDef_MessageSubDef(f), 0); | |
| upb_FieldPath_PutMapKey(&appender, ptr->map_key, key_f); | |
| ptr++; | |
| } else if (upb_FieldDef_IsRepeated(f)) { | |
| upb_FieldPath_Printf(&appender, "[%zu]", ptr->array_index); | |
| ptr++; | |
| } | |
| } | |
| // Advance beyond terminating NULL. | |
| ptr++; | |
| *path = ptr; | |
| return upb_FieldPath_NullTerminate(&appender, size); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // upb_util_HasUnsetRequired() | |
| //////////////////////////////////////////////////////////////////////////////// | |
| typedef struct { | |
| upb_FieldPathEntry* path; | |
| size_t size; | |
| size_t cap; | |
| } upb_FieldPathVector; | |
| typedef struct { | |
| upb_FieldPathVector stack; | |
| upb_FieldPathVector out_fields; | |
| const upb_DefPool* ext_pool; | |
| jmp_buf err; | |
| bool has_unset_required; | |
| bool save_paths; | |
| } upb_FindContext; | |
| static void upb_FieldPathVector_Init(upb_FieldPathVector* vec) { | |
| vec->path = NULL; | |
| vec->size = 0; | |
| vec->cap = 0; | |
| } | |
| static void upb_FieldPathVector_Reserve(upb_FindContext* ctx, | |
| upb_FieldPathVector* vec, | |
| size_t elems) { | |
| if (vec->cap - vec->size < elems) { | |
| const int oldsize = vec->cap * sizeof(*vec->path); | |
| size_t need = vec->size + elems; | |
| vec->cap = UPB_MAX(4, vec->cap); | |
| while (vec->cap < need) vec->cap *= 2; | |
| const int newsize = vec->cap * sizeof(*vec->path); | |
| vec->path = upb_grealloc(vec->path, oldsize, newsize); | |
| if (!vec->path) { | |
| UPB_LONGJMP(ctx->err, 1); | |
| } | |
| } | |
| } | |
| static void upb_FindContext_Push(upb_FindContext* ctx, upb_FieldPathEntry ent) { | |
| if (!ctx->save_paths) return; | |
| upb_FieldPathVector_Reserve(ctx, &ctx->stack, 1); | |
| ctx->stack.path[ctx->stack.size++] = ent; | |
| } | |
| static void upb_FindContext_Pop(upb_FindContext* ctx) { | |
| if (!ctx->save_paths) return; | |
| assert(ctx->stack.size != 0); | |
| ctx->stack.size--; | |
| } | |
| static void upb_util_FindUnsetInMessage(upb_FindContext* ctx, | |
| const upb_Message* msg, | |
| const upb_MessageDef* m) { | |
| // Iterate over all fields to see if any required fields are missing. | |
| for (int i = 0, n = upb_MessageDef_FieldCount(m); i < n; i++) { | |
| const upb_FieldDef* f = upb_MessageDef_Field(m, i); | |
| if (!upb_FieldDef_IsRequired(f)) continue; | |
| if (!msg || !upb_Message_HasFieldByDef(msg, f)) { | |
| // A required field is missing. | |
| ctx->has_unset_required = true; | |
| if (ctx->save_paths) { | |
| // Append the contents of the stack to the out array, then | |
| // NULL-terminate. | |
| upb_FieldPathVector_Reserve(ctx, &ctx->out_fields, ctx->stack.size + 2); | |
| if (ctx->stack.size) { | |
| memcpy(&ctx->out_fields.path[ctx->out_fields.size], ctx->stack.path, | |
| ctx->stack.size * sizeof(*ctx->stack.path)); | |
| } | |
| ctx->out_fields.size += ctx->stack.size; | |
| ctx->out_fields.path[ctx->out_fields.size++] = | |
| (upb_FieldPathEntry){.field = f}; | |
| ctx->out_fields.path[ctx->out_fields.size++] = | |
| (upb_FieldPathEntry){.field = NULL}; | |
| } | |
| } | |
| } | |
| } | |
| static void upb_util_FindUnsetRequiredInternal(upb_FindContext* ctx, | |
| const upb_Message* msg, | |
| const upb_MessageDef* m) { | |
| // OPT: add markers in the schema for where we can avoid iterating: | |
| // 1. messages with no required fields. | |
| // 2. messages that cannot possibly reach any required fields. | |
| upb_util_FindUnsetInMessage(ctx, msg, m); | |
| if (!msg) return; | |
| // Iterate over all present fields to find sub-messages that might be missing | |
| // required fields. This may revisit some of the fields already inspected | |
| // in the previous loop. We do this separately because this loop will also | |
| // find present extensions, which the previous loop will not. | |
| // | |
| // TODO: consider changing upb_Message_Next() to be capable of | |
| // visiting extensions only, for example with a kUpb_Message_BeginEXT | |
| // constant. | |
| size_t iter = kUpb_Message_Begin; | |
| const upb_FieldDef* f; | |
| upb_MessageValue val; | |
| while (upb_Message_Next(msg, m, ctx->ext_pool, &f, &val, &iter)) { | |
| // Skip non-submessage fields. | |
| if (!upb_FieldDef_IsSubMessage(f)) continue; | |
| upb_FindContext_Push(ctx, (upb_FieldPathEntry){.field = f}); | |
| const upb_MessageDef* sub_m = upb_FieldDef_MessageSubDef(f); | |
| if (upb_FieldDef_IsMap(f)) { | |
| // Map field. | |
| const upb_FieldDef* val_f = upb_MessageDef_Field(sub_m, 1); | |
| const upb_MessageDef* val_m = upb_FieldDef_MessageSubDef(val_f); | |
| if (!val_m) continue; | |
| const upb_Map* map = val.map_val; | |
| size_t iter = kUpb_Map_Begin; | |
| upb_MessageValue key, map_val; | |
| while (upb_Map_Next(map, &key, &map_val, &iter)) { | |
| upb_FindContext_Push(ctx, (upb_FieldPathEntry){.map_key = key}); | |
| upb_util_FindUnsetRequiredInternal(ctx, map_val.msg_val, val_m); | |
| upb_FindContext_Pop(ctx); | |
| } | |
| } else if (upb_FieldDef_IsRepeated(f)) { | |
| // Repeated field. | |
| const upb_Array* arr = val.array_val; | |
| for (size_t i = 0, n = upb_Array_Size(arr); i < n; i++) { | |
| upb_MessageValue elem = upb_Array_Get(arr, i); | |
| upb_FindContext_Push(ctx, (upb_FieldPathEntry){.array_index = i}); | |
| upb_util_FindUnsetRequiredInternal(ctx, elem.msg_val, sub_m); | |
| upb_FindContext_Pop(ctx); | |
| } | |
| } else { | |
| // Scalar sub-message field. | |
| upb_util_FindUnsetRequiredInternal(ctx, val.msg_val, sub_m); | |
| } | |
| upb_FindContext_Pop(ctx); | |
| } | |
| } | |
| static bool upb_util_DoHasUnsetRequired(upb_FindContext* ctx, | |
| const upb_Message* msg, | |
| const upb_MessageDef* m) { | |
| if (UPB_SETJMP(ctx->err) != 0) { | |
| return false; | |
| } | |
| upb_util_FindUnsetRequiredInternal(ctx, msg, m); | |
| return true; | |
| } | |
| bool upb_util_HasUnsetRequired(const upb_Message* msg, const upb_MessageDef* m, | |
| const upb_DefPool* ext_pool, | |
| upb_FieldPathEntry** fields, | |
| upb_Status* status) { | |
| upb_FindContext ctx; | |
| ctx.has_unset_required = false; | |
| ctx.save_paths = fields != NULL; | |
| ctx.ext_pool = ext_pool; | |
| upb_FieldPathVector_Init(&ctx.stack); | |
| upb_FieldPathVector_Init(&ctx.out_fields); | |
| if (upb_util_DoHasUnsetRequired(&ctx, msg, m)) { | |
| upb_gfree(ctx.stack.path); | |
| if (ctx.has_unset_required && fields) { | |
| upb_FieldPathVector_Reserve(&ctx, &ctx.out_fields, 1); | |
| ctx.out_fields.path[ctx.out_fields.size] = | |
| (upb_FieldPathEntry){.field = NULL}; | |
| *fields = ctx.out_fields.path; | |
| } | |
| return ctx.has_unset_required; | |
| } else { | |
| upb_gfree(ctx.stack.path); | |
| upb_gfree(ctx.out_fields.path); | |
| if (fields) { | |
| *fields = NULL; | |
| } | |
| if (status) { | |
| upb_Status_SetErrorMessage(status, "out of memory"); | |
| } | |
| return false; | |
| } | |
| } |