// Protocol Buffers - Google's data interchange format
// Copyright 2025 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 <stddef.h>
#include <stdint.h>

#include "upb/message/internal/message.h"
#include "upb/message/message.h"
#include "upb/mini_table/message.h"
#include "upb/wire/decode.h"
#include "upb/wire/decode_fast/cardinality.h"
#include "upb/wire/decode_fast/combinations.h"
#include "upb/wire/decode_fast/dispatch.h"
#include "upb/wire/decode_fast/field_parsers.h"
#include "upb/wire/eps_copy_input_stream.h"
#include "upb/wire/internal/decoder.h"

// Must be last.
#include "upb/port/def.inc"

typedef struct {
  intptr_t table;
  upb_Message* msg;
} fastdecode_submsgdata;

UPB_FORCEINLINE
const char* fastdecode_tosubmsg(upb_EpsCopyInputStream* e, const char* ptr,
                                int size, void* ctx) {
  upb_Decoder* d = (upb_Decoder*)e;
  fastdecode_submsgdata* submsg = ctx;
  ptr = upb_DecodeFast_Dispatch(d, ptr, submsg->msg, submsg->table, 0, 0);
  UPB_ASSUME(ptr != NULL);
  return ptr;
}

#define FASTDECODE_SUBMSG(d, ptr, msg, table, hasbits, data, type, card,       \
                          tagsize)                                             \
  int tagbytes = upb_DecodeFast_TagSizeBytes(tagsize);                         \
                                                                               \
  if (UPB_UNLIKELY(!fastdecode_checktag(data, tagbytes))) {                    \
    RETURN_GENERIC("submessage field tag mismatch\n");                         \
  }                                                                            \
                                                                               \
  if (--d->depth == 0) {                                                       \
    _upb_FastDecoder_ErrorJmp(d, kUpb_DecodeStatus_MaxDepthExceeded);          \
  }                                                                            \
                                                                               \
  upb_Message** dst;                                                           \
  uint32_t submsg_idx = (data >> 16) & 0xff;                                   \
  const upb_MiniTable* tablep = decode_totablep(table);                        \
  /* TODO: This is incorrect, but this code is currently dead. */ \
  /* We will fix it when we actually enable fast decode for message fields. */ \
  UPB_ASSERT(false);                                                           \
  const upb_MiniTable* subtablep =                                             \
      *UPB_PTR_AT(tablep, submsg_idx, const upb_MiniTable*);                   \
  fastdecode_submsgdata submsg = {decode_totable(subtablep)};                  \
  fastdecode_arr farr;                                                         \
                                                                               \
  if (subtablep->UPB_PRIVATE(table_mask) == (uint8_t)-1) {                     \
    d->depth++;                                                                \
    RETURN_GENERIC("submessage doesn't have fast tables.");                    \
  }                                                                            \
                                                                               \
  dst = fastdecode_getfield(d, ptr, msg, &data, &hasbits, &farr,               \
                            sizeof(upb_Message*), card);                       \
                                                                               \
  if (card == kUpb_DecodeFast_Scalar) {                                        \
    upb_DecodeFast_SetHasbits(msg, hasbits);                                   \
    hasbits = 0;                                                               \
  }                                                                            \
                                                                               \
  again:                                                                       \
  if (card == kUpb_DecodeFast_Repeated) {                                      \
    dst = fastdecode_resizearr(d, dst, &farr, sizeof(upb_Message*));           \
  }                                                                            \
                                                                               \
  submsg.msg = *dst;                                                           \
                                                                               \
  if (card == kUpb_DecodeFast_Repeated || UPB_LIKELY(!submsg.msg)) {           \
    *dst = submsg.msg = _upb_Message_New(subtablep, &d->arena);                \
  }                                                                            \
                                                                               \
  ptr += tagbytes;                                                             \
  ptr = fastdecode_delimited(d, ptr, fastdecode_tosubmsg, &submsg);            \
                                                                               \
  if (UPB_UNLIKELY(ptr == NULL || d->end_group != DECODE_NOGROUP)) {           \
    _upb_FastDecoder_ErrorJmp(d, kUpb_DecodeStatus_Malformed);                 \
  }                                                                            \
                                                                               \
  if (card == kUpb_DecodeFast_Repeated) {                                      \
    fastdecode_nextret ret = fastdecode_nextrepeated(                          \
        d, dst, &ptr, &farr, data, tagbytes, sizeof(upb_Message*));            \
    switch (ret.next) {                                                        \
      case FD_NEXT_SAMEFIELD:                                                  \
        dst = ret.dst;                                                         \
        goto again;                                                            \
      case FD_NEXT_OTHERFIELD:                                                 \
        d->depth++;                                                            \
        data = ret.tag;                                                        \
        UPB_MUSTTAIL return _upb_FastDecoder_TagDispatch(UPB_PARSE_ARGS);      \
      case FD_NEXT_ATLIMIT:                                                    \
        d->depth++;                                                            \
        return ptr;                                                            \
    }                                                                          \
  }                                                                            \
                                                                               \
  d->depth++;                                                                  \
  UPB_MUSTTAIL return upb_DecodeFast_Dispatch(UPB_PARSE_ARGS);

#define F(type, card, tagsize)                                        \
  UPB_PRESERVE_NONE const char* UPB_DECODEFAST_FUNCNAME(              \
      type, card, tagsize)(UPB_PARSE_PARAMS) {                        \
    FASTDECODE_SUBMSG(d, ptr, msg, table, hasbits, data,              \
                      kUpb_DecodeFast_##type, kUpb_DecodeFast_##card, \
                      kUpb_DecodeFast_##tagsize);                     \
  }

UPB_DECODEFAST_CARDINALITIES(UPB_DECODEFAST_TAGSIZES, F, Message)

#undef F
#undef FASTDECODE_SUBMSG
