// 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 #include #include #include #include #include #include #include #include "upb/base/descriptor_constants.h" #include "upb/message/array.h" #include "upb/message/map.h" #include "upb/message/types.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) { size_t need = vec->size + elems; vec->cap = UPB_MAX(4, vec->cap); while (vec->cap < need) vec->cap *= 2; vec->path = realloc(vec->path, vec->cap * sizeof(*vec->path)); 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_Label(f) != kUpb_Label_Required) 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); } } bool upb_util_HasUnsetRequired(const upb_Message* msg, const upb_MessageDef* m, const upb_DefPool* ext_pool, upb_FieldPathEntry** fields) { 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); upb_util_FindUnsetRequiredInternal(&ctx, msg, m); free(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; }