#pragma once

/**
 * @file onnx.h
 * @brief ONNX proto message classes — the lightweight, protobuf-free ONNX schema.
 *
 * Every class in this file is generated from the macros defined in stream_class.h
 * (BEGIN_PROTO / END_PROTO / FIELD* / SERIALIZATION_METHOD).  Each class inherits
 * from ONNX_LIGHT_NAMESPACE::Message and provides parse/serialize methods as well as typed field
 * accessors following the pattern described in the API documentation.
 */

#include "fields.h"
#include "onnx_light_helpers.h"
#include "stream.h"
#include "stream_class.h"

namespace ONNX_LIGHT_NAMESPACE {

/** Indicates whether an operator is experimental or stable in the ONNX spec. */
enum OperatorStatus : int { EXPERIMENTAL = 0, STABLE = 1 };

// StringStringEntryProto

// message StringStringEntryProto {
//   optional string key = 1;
//   optional string value = 2;
// }
BEGIN_PROTO(StringStringEntryProto, "Defines a key value pair, both defines a string.")
FIELD_STR(key, 1, "the key")
FIELD_STR(value, 2, "the value")
END_PROTO()

// message IntIntListEntryProto {
//   optional int64 key = 1;
//   repeated int64 value = 2;
// }
BEGIN_PROTO(IntIntListEntryProto,
            "Defines a key value pair, key is an integer, value is a list of integers.")
FIELD_DEFAULT(int64_t, key, 1, 0, "the key")
FIELD_REPEATED(int64_t, value, 2, "the value is a list of integers")
END_PROTO()

// message TensorAnnotation {
//   optional string tensor_name = 1;
//   repeated StringStringEntryProto quant_parameter_tensor_names = 2;
// }
BEGIN_PROTO(TensorAnnotation, "Defines a tensor annotation, useful for quantized tensors.")
FIELD_STR(tensor_name, 1, "tensor name")
FIELD_REPEATED_PROTO(
    StringStringEntryProto, quant_parameter_tensor_names, 2,
    "<key, value> pairs to annotate tensor specified by <tensor_name> above. The "
    "keys used in the mapping below must be pre-defined in ONNX spec. For example, "
    "for 8-bit linear quantization case, 'SCALE_TENSOR', 'ZERO_POINT_TENSOR' will "
    "be pre-defined as quantization parameter keys.")
END_PROTO()

// DeviceConfigurationProto

// message DeviceConfigurationProto {
//     optional string name = 1;
//     optional int32 num_devices = 2;
//     repeated string device = 3;
// }
BEGIN_PROTO(DeviceConfigurationProto, "Describes a multi-device configuration for a model.")
FIELD_STR(name, 1,
          "This field MUST be present for this version of the IR. Name of the configuration.")
FIELD_DEFAULT(int32_t, num_devices, 2, 0,
              "This field MUST be present for this version of the IR. Number of devices inside "
              "this configuration.")
FIELD_REPEATED(utils::String, device, 3,
               "Optional names of the devices. MUST be length of num_devices if provided.")
END_PROTO()

// SimpleShardedDimProto

// message SimpleShardedDimProto {
//     oneof dim {
//         int64 dim_value = 1;
//         string dim_param = 2;
//     }
//     optional int64 num_shards = 3;
// }
BEGIN_PROTO(SimpleShardedDimProto,
            "Indicates that N blocks are divided into M shards. N is allowed to be symbolic "
            "where M is required to be a constant.")
FIELD_OPTIONAL(int64_t, dim_value, 1, "Dimension value to be sharded if it is a fixed value.")
FIELD_STR(dim_param, 2, "Dimension name to be sharded if it is a dynamic value.")
FIELD_DEFAULT(int64_t, num_shards, 3, 0,
              "This field MUST be present for this version of the IR. Number of shards to "
              "split dim into.")
END_PROTO()

// message ShardedDimProto {
//   optional int64 axis = 1;
//   repeated SimpleShardedDimProto simple_sharding = 2;
// }
BEGIN_PROTO_NOINIT(ShardedDimProto,
                   "Describes the sharding spec for a single axis of a sharded tensor.")
inline ShardedDimProto() : axis_(0) {}
FIELD(int64_t, axis, 1,
      "This field MUST be present for this version of the IR. The axis this sharding "
      "corresponds to. Must be in the range of [-r, r - 1], where r is the rank of the tensor. "
      "Negative axis values means counting from the back.")
FIELD_REPEATED_PROTO(
    SimpleShardedDimProto, simple_sharding, 2,
    "Describes how the tensor on the provided axis is sharded. The common-case is described by "
    "a single instance of SimpleShardedDimProto. Multiple instances can be used to handle "
    "cases where a sharded tensor is reshaped, fusing multiple axes into one.")
END_PROTO()

// ShardingSpecProto

// message ShardingSpecProto {
//   optional string tensor_name = 1;
//   repeated int64 device = 2;
//   repeated IntIntListEntryProto index_to_device_group_map = 3;
//   repeated ShardedDimProto sharded_dim = 4;
// }
BEGIN_PROTO(ShardingSpecProto,
            "Describes the sharding spec for a specific, input or output tensor of a node.")
FIELD_STR(
    tensor_name, 1,
    "This field MUST be present for this version of the IR. Identifies the input or output of "
    "the node that is being sharded. Required to match a name specified in the node's input or "
    "output list of ValueInfoProtos. It is called `logical tensor` in subsequent descriptions.")
FIELD_REPEATED(int64_t, device, 2,
               "The following is the list of devices across which the logical tensor is "
               "sharded or replicated.")
FIELD_REPEATED_PROTO(
    IntIntListEntryProto, index_to_device_group_map, 3,
    "Each element v in above field devices may represent either a device or a set of devices "
    "(when we want the same shard/tensor to be replicated across a subset of devices), as "
    "indicated by the following optional map. If the map contains an entry for v, then v "
    "represents a device group, and the map indicates the set of devices in that group.")
FIELD_REPEATED_PROTO(ShardedDimProto, sharded_dim, 4,
                     "The following is the sharded-shape of the tensor, consisting of the "
                     "sharding-spec for each axis of the tensor.")
END_PROTO()

// NodeDeviceConfigurationProto

// message NodeDeviceConfigurationProto {
//     optional string configuration_id = 1;
//     repeated ShardingSpecProto sharding_spec = 2;
//     optional int32 pipeline_stage = 3;
// }
BEGIN_PROTO(NodeDeviceConfigurationProto,
            "Defines a multi-device configuration proto for NodeProto.")
FIELD_STR(configuration_id, 1,
          "This field MUST be present for this version of the IR., ID of the configuration. "
          "MUST match the name of a DeviceConfigurationProto.")
FIELD_REPEATED_PROTO(ShardingSpecProto, sharding_spec, 2, "Sharding spec for the node.")
FIELD_OPTIONAL(int32_t, pipeline_stage, 3, "Pipeline stage of this node.")
END_PROTO()

// message OperatorSetIdProto {
//   optional string domain = 1;
//   optional int64 version = 2;
// }
BEGIN_PROTO(OperatorSetIdProto,
            "Defines a unique pair domain, opset version for a set of operators.")
FIELD_STR(domain, 1,
          "The domain of the operator set being identified. The empty string ("
          ") or absence of this field implies the operator set that is defined as part of the "
          "ONNX specification. This field MUST be present in this version of the IR when "
          "referring to any other operator set.")
FIELD_DEFAULT(int64_t, version, 2, 0,
              "The version of the operator set being identified. This field MUST be present in "
              "this version of the IR.")
END_PROTO()

// TensorShapeProto

// message TensorShapeProto {
//   message Dimension {
//     oneof value {
//       int64 dim_value = 1;
//       string dim_param = 2;
//     };
//     optional string denotation = 3;
//   };
//   repeated Dimension dim = 1;
// }
BEGIN_PROTO_NOINIT(TensorShapeProto,
                   "Defines a tensor shape. A dimension can be either an integer value or a "
                   "symbolic variable. A symbolic variable represents an unknown dimension.")
// message Dimension {
//   oneof value {
//     int64 dim_value = 1;
//     string dim_param = 2;
//   };
//   optional string denotation = 3;
// }
BEGIN_PROTO(Dimension, "Defines a dimension, it can be fixed (an integer dim_value) or dynamic "
                       "(a string dim_param). Only one of them can be set.")
FIELD_OPTIONAL(int64_t, dim_value, 1, "Dimension value if it is a fixed value.")
FIELD_STR(dim_param, 2, "Dimension name if it is a dynamic value.")
FIELD_STR(denotation, 3,
          "Standard denotation can optionally be used to denote tensor dimensions with standard "
          "semantic descriptions to ensure that operations are applied to the correct axis of a "
          "tensor. Refer to https://github.com/onnx/onnx/blob/main/docs/DimensionDenotation.md"
          "#axisdenotation-definition for pre-defined dimension denotations.")
enum ValueCase : int32_t {
  VALUE_NOT_SET = 0,
  kDimValue = 1,
  kDimParam = 2,
};
inline ValueCase value_case() const {
  if (has_dim_value())
    return ValueCase::kDimValue;
  if (has_dim_param())
    return ValueCase::kDimParam;
  return ValueCase::VALUE_NOT_SET;
}
END_PROTO()
inline TensorShapeProto() {}
FIELD_REPEATED_PROTO(Dimension, dim, 1, "Shape as a list of Dimension.")
inline void Clear() { clr_dim(); }
END_PROTO()

// TensorProto

// message TensorProto {
//   enum DataType {
//     UNDEFINED = 0;
//     FLOAT = 1;
//     UINT8 = 2;
//     INT8 = 3;
//     UINT16 = 4;
//     INT16 = 5;
//     INT32 = 6;
//     INT64 = 7;
//     STRING = 8;
//     BOOL = 9;
//     FLOAT16 = 10;
//     DOUBLE = 11;
//     UINT32 = 12;
//     UINT64 = 13;
//     COMPLEX64 = 14;
//     COMPLEX128 = 15;
//     BFLOAT16 = 16;
//     FLOAT8E4M3FN = 17;
//     FLOAT8E4M3FNUZ = 18;
//     FLOAT8E5M2 = 19;
//     FLOAT8E5M2FNUZ = 20;
//     UINT4 = 21;
//     INT4 = 22;
//     FLOAT4E2M1 = 23;
//     FLOAT8E8M0 = 24;
//     UINT2 = 25;
//     INT2 = 26;
//   }
//   repeated int64 dims = 1;
//   optional int32 data_type = 2;
//   message Segment {
//     optional int64 begin = 1;
//     optional int64 end = 2;
//   }
//   optional Segment segment = 3;
//   repeated float float_data = 4 [packed = true];
//   repeated int32 int32_data = 5 [packed = true];
//   repeated bytes string_data = 6;
//   repeated int64 int64_data = 7 [packed = true];
//   optional string name = 8;
//   optional string doc_string = 12;
//   optional bytes raw_data = 9;
//   repeated StringStringEntryProto external_data = 13;
//   enum DataLocation {
//     DEFAULT = 0;
//     EXTERNAL = 1;
//   }
//   optional DataLocation data_location = 14;
//   repeated double double_data = 10 [packed = true];
//   repeated uint64 uint64_data = 11 [packed = true];
//   repeated StringStringEntryProto metadata_props = 16;
// }
BEGIN_PROTO_NOINIT(TensorProto, "Defines a tensor and its content.")
enum DataType : int32_t {
  UNDEFINED = 0,
  // Basic types.
  FLOAT = 1,  // float
  UINT8 = 2,  // uint8_t
  INT8 = 3,   // int8_t
  UINT16 = 4, // uint16_t
  INT16 = 5,  // int16_t
  INT32 = 6,  // int32_t
  INT64 = 7,  // int64_t
  STRING = 8, // string
  BOOL = 9,   // bool

  // IEEE754 half-precision floating-point format (16 bits wide).
  // This format has 1 sign bit, 5 exponent bits, and 10 mantissa bits.
  FLOAT16 = 10,

  DOUBLE = 11,
  UINT32 = 12,
  UINT64 = 13,
  COMPLEX64 = 14,  // complex with float32 real and imaginary components
  COMPLEX128 = 15, // complex with float64 real and imaginary components

  // Non-IEEE floating-point format based on IEEE754 single-precision
  // floating-point number truncated to 16 bits.
  // This format has 1 sign bit, 8 exponent bits, and 7 mantissa bits.
  BFLOAT16 = 16,

  // Non-IEEE floating-point format based on papers
  // FP8 Formats for Deep Learning, https://arxiv.org/abs/2209.05433,
  // 8-bit Numerical Formats For Deep Neural Networks, https://arxiv.org/pdf/2206.02915.pdf.
  // Operators supported FP8 are Cast, CastLike, QuantizeLinear, DequantizeLinear.
  // The computation usually happens inside a block quantize / dequantize
  // fused by the runtime.
  FLOAT8E4M3FN = 17,   // float 8, mostly used for coefficients, nan, no inf
  FLOAT8E4M3FNUZ = 18, // float 8, mostly used for coefficients, nan, no inf, no negative zero
  FLOAT8E5M2 = 19,     // follows IEEE 754, supports nan, inf
  FLOAT8E5M2FNUZ = 20, // follows IEEE 754, supports nan, no inf, no negative zero
                       // no negative zero

  // 4-bit integer data types
  UINT4 = 21, // Unsigned integer in range [0, 15]
  INT4 = 22,  // Signed integer in range [-8, 7], using two's-complement representation

  // 4-bit floating point data types
  FLOAT4E2M1 = 23,

  // E8M0 type used as the scale for microscaling (MX) formats:
  // https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf
  FLOAT8E8M0 = 24,

  // 2-bit integer data type
  UINT2 = 25, // Unsigned integer in range [0, 3]
  INT2 = 26,  // Signed integer in range [-2, 1], using two's complement representation

  // Flat protobuf-style enumerator aliases (drop-in compatibility with the
  // protobuf-generated header, which exposes names such as
  // TensorProto_DataType_FLOAT both at namespace scope and as members of the
  // enum type).  Duplicate values are permitted in a C++ enumeration.
  TensorProto_DataType_UNDEFINED = 0,
  TensorProto_DataType_FLOAT = 1,
  TensorProto_DataType_UINT8 = 2,
  TensorProto_DataType_INT8 = 3,
  TensorProto_DataType_UINT16 = 4,
  TensorProto_DataType_INT16 = 5,
  TensorProto_DataType_INT32 = 6,
  TensorProto_DataType_INT64 = 7,
  TensorProto_DataType_STRING = 8,
  TensorProto_DataType_BOOL = 9,
  TensorProto_DataType_FLOAT16 = 10,
  TensorProto_DataType_DOUBLE = 11,
  TensorProto_DataType_UINT32 = 12,
  TensorProto_DataType_UINT64 = 13,
  TensorProto_DataType_COMPLEX64 = 14,
  TensorProto_DataType_COMPLEX128 = 15,
  TensorProto_DataType_BFLOAT16 = 16,
  TensorProto_DataType_FLOAT8E4M3FN = 17,
  TensorProto_DataType_FLOAT8E4M3FNUZ = 18,
  TensorProto_DataType_FLOAT8E5M2 = 19,
  TensorProto_DataType_FLOAT8E5M2FNUZ = 20,
  TensorProto_DataType_UINT4 = 21,
  TensorProto_DataType_INT4 = 22,
  TensorProto_DataType_FLOAT4E2M1 = 23,
  TensorProto_DataType_FLOAT8E8M0 = 24,
  TensorProto_DataType_UINT2 = 25,
  TensorProto_DataType_INT2 = 26,

  // Future extensions go here.
};

// A value is considered valid only if it falls within the contiguous range of enum literals
// declared above (UNDEFINED(0) .. INT2(26)); this matches the range-checking semantics of the
// switch statement real protobuf generates for TensorProto_DataType_IsValid. The previous
// implementation only rejected the single value UNDEFINED(0) and accepted *any* other int,
// including out-of-range/garbage values such as -100 coming from a corrupted or malicious
// protobuf; callers (e.g. onnxruntime's "does not have valid data type" check in graph.cc) rely
// on this function to reject such values before they propagate further (e.g. into
// DataTypeUtils::ToDataTypeString, which throws an uncaught exception for out-of-range values).
inline static constexpr bool DataType_IsValid(DataType t) {
  return static_cast<int32_t>(t) >= static_cast<int32_t>(DataType::UNDEFINED) &&
         static_cast<int32_t>(t) <= static_cast<int32_t>(DataType::INT2);
}
/** Overload accepting int for protobuf compatibility. */
inline static constexpr bool DataType_IsValid(int t) {
  return DataType_IsValid(static_cast<DataType>(t));
}
inline static constexpr const char *DataType_Name(DataType t) {
  switch (t) {
  case FLOAT:
    return "FLOAT";
  case UINT8:
    return "UINT8";
  case INT8:
    return "INT8";
  case UINT16:
    return "UINT16";
  case INT16:
    return "INT16";
  case INT32:
    return "INT32";
  case INT64:
    return "INT64";
  case STRING:
    return "STRING";
  case BOOL:
    return "BOOL";
  case FLOAT16:
    return "FLOAT16";
  case DOUBLE:
    return "DOUBLE";
  case UINT32:
    return "UINT32";
  case UINT64:
    return "UINT64";
  case COMPLEX64:
    return "COMPLEX64";
  case COMPLEX128:
    return "COMPLEX128";
  case BFLOAT16:
    return "BFLOAT16";
  case FLOAT8E4M3FN:
    return "FLOAT8E4M3FN";
  case FLOAT8E4M3FNUZ:
    return "FLOAT8E4M3FNUZ";
  case FLOAT8E5M2:
    return "FLOAT8E5M2";
  case FLOAT8E5M2FNUZ:
    return "FLOAT8E5M2FNUZ";
  case UINT4:
    return "UINT4";
  case INT4:
    return "INT4";
  case FLOAT4E2M1:
    return "FLOAT4E2M1";
  case FLOAT8E8M0:
    return "FLOAT8E8M0";
  case UINT2:
    return "UINT2";
  case INT2:
    return "INT2";
  case UNDEFINED:
  default:
    return "UNDEFINED";
  }
}
/** Overload accepting int for protobuf compatibility. */
inline static constexpr const char *DataType_Name(int t) {
  return DataType_Name(static_cast<DataType>(t));
}

enum DataLocation : int32_t {
  DEFAULT = 0,
  EXTERNAL = 1,
  // Flat protobuf-style enumerator aliases (drop-in compatibility).
  TensorProto_DataLocation_DEFAULT = 0,
  TensorProto_DataLocation_EXTERNAL = 1
};

// message Segment {
//   optional int64 begin = 1;
//   optional int64 end = 2;
// }
BEGIN_PROTO(
    Segment,
    "For very large tensors, we may want to store them in chunks, in which case the following "
    "fields will specify the segment that is stored in the current TensorProto.")
FIELD_DEFAULT(int64_t, begin, 1, 0, "Segment start.")
FIELD_DEFAULT(int64_t, end, 2, 0, "Segment end.")
END_PROTO()

FIELD_REPEATED(int64_t, dims, 1, "The shape of the tensor.")
FIELD(DataType, data_type, 2,
      "The data type of the tensor. This field MUST have a valid TensorProto.DataType value")
FIELD_OPTIONAL(
    Segment, segment, 3,
    "For very large tensors, we may want to store them in chunks, in which case the "
    "following fields will specify the segment that is stored in the current TensorProto.")
FIELD_REPEATED_PACKED(
    float, float_data, 4,
    "Tensor content must be organized in row-major order. Depending on the data_type field, "
    "exactly one of the fields below with name ending in _data is used to store the elements "
    "of the tensor. For float and complex64 values Complex64 tensors are encoded as a single "
    "array of floats, with the real components appearing in odd numbered positions, and the "
    "corresponding imaginary component appearing in the subsequent even numbered position. "
    "(e.g., [1.0 + 2.0i, 3.0 + 4.0i] is encoded as [1.0, 2.0 ,3.0 ,4.0] When this field is "
    "present, the data_type field MUST be FLOAT or COMPLEX64.")
FIELD_REPEATED_PACKED(
    int32_t, int32_data, 5,
    "For int32, uint8, int8, uint16, int16, uint4, int4, bool, (b)float16, float8, and "
    "float4: - (b)float16 and float8 values MUST be converted bit-wise into an unsigned "
    "integer representation before being written to the buffer. - Each pair of uint4, int4, "
    "and float4 values MUST be packed as two 4-bit elements into a single byte. The first "
    "element is stored in the 4 least significant bits (LSB), and the second element is "
    "stored in the 4 most significant bits (MSB). Consequently: - For data types with a "
    "bit-width of 8 or greater, each `int32_data` stores one element. - For 4-bit data "
    "types, each `int32_data` stores two elements. When this field is present, the data_type "
    "field MUST be INT32, INT16, INT8, INT4, UINT16, UINT8, UINT4, BOOL, FLOAT16, BFLOAT16, "
    "FLOAT8E4M3FN, FLOAT8E4M3FNUZ, FLOAT8E5M2, FLOAT8E5M2FNUZ, FLOAT8E8M0, FLOAT4E2M1, "
    "UINT2, INT2")
FIELD_REPEATED_STR(utils::String, string_data, 6,
                   "For strings. Each element of string_data is a UTF-8 encoded Unicode string. No "
                   "trailing null, no leading BOM. The 'string' scalar type is not used to match "
                   "ML community conventions. When this field is "
                   "present, the data_type field MUST be STRING")
FIELD_REPEATED_PACKED(int64_t, int64_data, 7,
                      "For int64. When this field is present, the data_type field MUST be INT64")
FIELD_STR(name, 8, "Optionally, a name for the tensor.")
FIELD_BYTES(
    raw_data, 9,
    "Serializations can either use one of the fields above, or use this raw bytes field. The "
    "only exception is the string case, where one is required to store the content in the "
    "repeated bytes string_data field. When this raw_data field is used to store tensor "
    "value, elements MUST be stored in as fixed-width, little-endian order. Floating-point "
    "data types MUST be stored in IEEE 754 format. Complex64 elements must be written as two "
    "consecutive FLOAT values, real component first. Complex128 elements must be written as "
    "two consecutive DOUBLE values, real component first. Boolean type MUST be written one "
    "byte per tensor element (00000001 for true, 00000000 for false). uint4 and int4 values "
    "must be packed to 4bitx2, the first element is stored in the 4 LSB and the second "
    "element is stored in the 4 MSB. Note: the advantage of specific field rather than the "
    "raw_data field is that in some cases (e.g. int data), protobuf does a better packing "
    "via variable length storage, and may lead to smaller binary footprint. When this field "
    "is present, the data_type field MUST NOT be STRING or UNDEFINED.")
FIELD_REPEATED_PACKED(
    double, double_data, 10,
    "For double Complex128 tensors are encoded as a single array of doubles, with the real "
    "components appearing in odd numbered positions, and the corresponding imaginary component "
    "appearing in the subsequent even numbered position. (e.g., [1.0 + 2.0i, 3.0 + 4.0i] is "
    "encoded as [1.0, 2.0 ,3.0 ,4.0] When this field is present, the data_type field MUST be "
    "DOUBLE or COMPLEX128.")
FIELD_REPEATED_PACKED(uint64_t, uint64_data, 11,
                      "For uint64 and uint32 values. When this field is present, the data_type "
                      "field MUST be UINT32 or UINT64.")
FIELD_STR(doc_string, 12, "A human-readable documentation for this tensor. Markdown is allowed.")
FIELD_REPEATED_PROTO(
    StringStringEntryProto, external_data, 13,
    "Data can be stored inside the protobuf file using type-specific fields or raw_data. "
    "Alternatively, raw bytes data can be stored in an external file, using the external_data "
    "field. external_data stores key-value pairs describing data location. Recognized keys "
    "are: "
    "- location (required) - POSIX filesystem path relative to the directory where "
    "the ONNX protobuf model was stored "
    "- offset (optional) - position of byte at which stored data begins. Integer stored as "
    "string. "
    "Offset values SHOULD be multiples 4096 (page size) to enable mmap support. "
    "- length (optional) - number of bytes containing data. Integer stored as string. "
    "- checksum (optional) - SHA1 digest of file specified in under 'location' key.")
FIELD_OPTIONAL_ENUM(
    DataLocation, data_location, 14,
    "Location of the data for this tensor. MUST be one of: - DEFAULT - data stored inside the "
    "protobuf message. Data is stored in raw_data (if set) otherwise in type-specified field. "
    "- EXTERNAL - data stored in an external location as described by external_data field. If "
    "value not set, data is stored in raw_data (if set) otherwise in type-specified field.")
FIELD_REPEATED_PROTO(StringStringEntryProto, metadata_props, 16,
                     "Named metadata values; keys should be distinct.")
inline TensorProto() { data_type_ = DataType::UNDEFINED; }
inline void Clear() {
  this->~TensorProto();
  new (this) TensorProto();
}
/** Sets raw_data from a byte buffer (protobuf bytes-field compat). */
inline void set_raw_data(const void *data, size_t size) {
  raw_data_.resize(size);
  if (size > 0)
    std::memcpy(raw_data_.data(), data, size);
}
/** Sets raw_data from a std::string (protobuf bytes-field compat). */
inline void set_raw_data(const std::string &data) { set_raw_data(data.data(), data.size()); }
inline void clear_raw_data() { raw_data_.clear(); }
inline void set_data_type(int v) { data_type_ = static_cast<DataType>(v); }
/** Returns whether raw_data is present, including an explicitly set empty value. */
inline bool is_raw_data() const { return has_raw_data(); }
/**
 * Sets the tensor raw data to a borrowed view of an external buffer and attaches
 * a custom deleter called when all references to that buffer are dropped.
 *
 * Stores ptr/sz as a borrowed view (identical to calling
 * ``ref_raw_data().assign_borrowed(ptr, sz)``), but additionally schedules
 * deleter() for invocation when the last copy of the internal owner token is
 * destroyed (i.e., when the TensorProto, and all copies of it sharing the same
 * buffer, go out of scope or are overwritten).
 *
 * The deleter receives no arguments and returns void.  Any callable — lambda,
 * function pointer, or functor — is accepted.
 *
 * @param ptr     Pointer to the first byte of tensor raw data.
 * @param sz      Number of raw data bytes.
 * @param deleter Callable invoked once when the backing storage is released.
 */
template <typename Deleter>
inline void set_raw_data_with_deleter(const uint8_t *ptr, size_t sz, Deleter &&deleter) {
  raw_data_.assign_with_deleter(ptr, sz, std::forward<Deleter>(deleter));
}
/**
 * Attaches a custom deleter to this tensor's ``raw_data`` without changing the stored bytes.
 *
 * Unlike ``set_raw_data_with_deleter``, the current ``raw_data`` pointer/size and storage mode
 * (owned or borrowed) are left untouched; only a cleanup callback is registered.  deleter() is
 * invoked once when the last copy of the internal owner token is destroyed (i.e., when the
 * TensorProto, and all copies of it sharing the same buffer, go out of scope or the buffer is
 * overwritten/cleared).  Any previously attached deleter/owner token is replaced.
 *
 * The deleter receives no arguments and returns void.  Any callable — lambda, function pointer,
 * or functor — is accepted.
 *
 * @param deleter Callable invoked once when the backing storage is released.
 */
template <typename Deleter> inline void attach_raw_data_deleter(Deleter &&deleter) {
  raw_data_.attach_deleter(std::forward<Deleter>(deleter));
}
/**
 * Loads the raw bytes of this tensor from the external file described by
 * its ``external_data`` field into ``raw_data``.
 *
 * Recognized ``external_data`` keys are ``location`` (required), ``offset``
 * (optional) and ``length`` (optional, also accepted as ``size``).
 * The tensor must have ``data_location == EXTERNAL``.
 *
 * The ``external_data`` and ``data_location`` fields are preserved so the
 * tensor still describes where the data originally lives.
 *
 * @param base_dir Directory containing the external data file. The final
 *        path is ``base_dir / location``. When empty, ``location`` is used
 *        as-is.
 */
void LoadExternalData(const std::string &base_dir = "");
/**
 * Computes a hash of the tensor content, ignoring the tensor ``name``.
 *
 * The element type and shape are always mixed in, followed by the size of
 * every payload field (raw_data, the typed data arrays, string_data and
 * external_data). When ``include_content`` is true the payload bytes and
 * string/external entries themselves are hashed as well, producing a more
 * discriminating (but costlier) value; when false only the field sizes are
 * mixed in, yielding a cheap hash suitable for bucketing candidates that are
 * then compared exactly.
 *
 * @param include_content When true, hashes the payload bytes; when false,
 *        hashes only the payload sizes.
 * Returns: A 64-bit hash of the selected fields.
 */
int64_t ContentHash(bool include_content) const;
END_PROTO()

// SparseTensorProto

// message SparseTensorProto {
//   optional TensorProto values = 1;
//   optional TensorProto indices = 2;
//   repeated int64 dims = 3;
// }
BEGIN_PROTO(SparseTensorProto, "A sparse tensor.")
FIELD(TensorProto, values, 1,
      "The sequence of non-default values are encoded as a tensor of shape [NNZ]. The "
      "default-value is zero for numeric tensors, and empty-TypeProto::TensorString for string "
      "tensors. values must have a non-empty name present which serves as a name for "
      "SparseTensorProto when used in sparse_initializer list.")
FIELD(TensorProto, indices, 2,
      "The indices of the non-default values, which may be stored in one of two formats. (a) "
      "Indices can be a tensor of shape [NNZ, rank] with the [i,j]-th value corresponding to "
      "the j-th index of the i-th value (in the values tensor). (b) Indices can be a tensor of "
      "shape [NNZ], in which case the i-th value must be the linearized-index of the i-th "
      "value (in the values tensor). The linearized-index can be converted into an index tuple "
      "(k_1,...,k_rank) using the shape provided below. The indices must appear in ascending "
      "order without duplication. In the first format, the ordering is lexicographic-ordering: "
      "e.g., index-value [1,4] must appear before [2,1].")
FIELD_REPEATED(int64_t, dims, 3, "The shape.")
END_PROTO()

// TypeProto

// message TypeProto {
//   message Tensor {
//     optional int32 elem_type = 1;
//     optional TensorShapeProto shape = 2;
//   }
//   message Sequence {
//     optional TypeProto elem_type = 1;
//   };
//   message Map {
//     optional int32 key_type = 1;
//     optional TypeProto value_type = 2;
//   };
//   message Optional {
//     optional TypeProto elem_type = 1;
//   };
//   message SparseTensor {
//     optional int32 elem_type = 1;
//     optional TensorShapeProto shape = 2;
//   }
//   message Opaque {
//     optional string domain = 1;
//     optional string name = 2;
//   }
//   oneof value {
//     Tensor tensor_type = 1;
//     Sequence sequence_type = 4;
//     Map map_type = 5;
//     Optional optional_type = 9;
//     SparseTensor sparse_tensor_type = 8;
//     Opaque opaque_type = 7;
//   }
//   optional string denotation = 6;
// }
BEGIN_PROTO_NOINIT(TypeProto, "Defines a type, it can be a tensor type (element type and "
                              "shape), a sequence of the same element type, ...")

// message Tensor {
//   optional int32 elem_type = 1;
//   optional TensorShapeProto shape = 2;
// }
BEGIN_PROTO(Tensor, "Defines a tensor type (element type, shape).")
FIELD_OPTIONAL_ENUM(
    TensorProto::DataType, elem_type, 1,
    "This field MUST NOT have the value of UNDEFINED. This field MUST have a valid "
    "TensorProto.DataType value. This field MUST be present for this version of the IR.")
FIELD_OPTIONAL(TensorShapeProto, shape, 2, "The shape.")
inline void set_elem_type(int v) { elem_type_ = static_cast<TensorProto::DataType>(v); }
END_PROTO()

// message SparseTensor {
//   optional int32 elem_type = 1;
//   optional TensorShapeProto shape = 2;
// }
BEGIN_PROTO(SparseTensor, "Defines a sparse tensor type (element type, shape)")
FIELD_OPTIONAL_ENUM(
    TensorProto::DataType, elem_type, 1,
    "This field MUST NOT have the value of UNDEFINED. This field MUST have a valid "
    "TensorProto.DataType value. This field MUST be present for this version of the IR.")
FIELD_OPTIONAL(TensorShapeProto, shape, 2, "The shape.")
inline void set_elem_type(int v) { elem_type_ = static_cast<TensorProto::DataType>(v); }
END_PROTO()

// message Sequence {
//   optional TypeProto elem_type = 1;
// }
BEGIN_PROTO(Sequence, "Defines the type of each element in a sequence.")
FIELD_OPTIONAL(TypeProto, elem_type, 1,
               "The type and optional shape of each element of the sequence. This field MUST "
               "be present for this version of the IR.")
END_PROTO()

// message Optional {
//   optional TypeProto elem_type = 1;
// }
BEGIN_PROTO(Optional, "Defines the type of an optional value.")
FIELD_OPTIONAL(
    TypeProto, elem_type, 1,
    "The type and optional shape of the element wrapped. This field MUST be present for this "
    "version of the IR. Possible values correspond to OptionalProto.DataType enum")
END_PROTO()

// message Map {
//   optional int32 key_type = 1;
//   optional TypeProto value_type = 2;
// }
BEGIN_PROTO(Map, "Defines the type of the key and the type of each value in a dictionary.")
FIELD_DEFAULT(
    int32_t, key_type, 1, -1,
    "This field MUST have a valid TensorProto.DataType value. This field MUST be present for "
    "this version of the IR. This field MUST refer to an integral type ([U]INT{8|16|32|64}) "
    "or STRING optional int32 key_type = 1;")
FIELD_OPTIONAL(TypeProto, value_type, 2, "This field MUST be present for this version of the IR.")
END_PROTO()

// message Opaque {
//   optional string domain = 1;
//   optional string name = 2;
// }
BEGIN_PROTO(Opaque, "Defines an opaque, runtime-specific type identified by a domain and name.")
FIELD_STR(domain, 1, "The domain of the opaque type.")
FIELD_STR(name, 2, "The name of the opaque type.")
END_PROTO()

inline TypeProto() {}
FIELD_OPTIONAL_ONEOF(Tensor, tensor_type, 1, type, "The type of a tensor.")
FIELD_OPTIONAL_ONEOF(Sequence, sequence_type, 4, type, "The type of a sequence.")
FIELD_OPTIONAL_ONEOF(Map, map_type, 5, type, "The type of a map.")
FIELD_OPTIONAL_ONEOF(Opaque, opaque_type, 7, type, "The type of an opaque object.")
FIELD_STR(denotation, 6,
          "An optional denotation can be used to denote the whole type with a standard "
          "semantic description as to what is stored inside. Refer to "
          "https://github.com/onnx/onnx/blob/main/docs/"
          "TypeDenotation.md#type-denotation-definition for pre-defined type denotations.")
FIELD_OPTIONAL_ONEOF(SparseTensor, sparse_tensor_type, 8, type, "Type of the sparse tensor")
FIELD_OPTIONAL_ONEOF(Optional, optional_type, 9, type, "The type of an optional.")
inline bool has_type() const {
  return has_tensor_type() || has_sequence_type() || has_map_type() || has_sparse_tensor_type() ||
         has_optional_type() || has_opaque_type();
}
inline bool is_set() const {
  return has_tensor_type() || has_sparse_tensor_type() || has_sequence_type() ||
         has_optional_type() || has_map_type() || has_opaque_type();
}
enum ValueCase : int32_t {
  kUndefined = 0,
  kTensorType = 1,
  kSparseTensorType = 2,
  kSequenceType = 3,
  kMapType = 4,
  kOptionalType = 5,
  kOpaqueType = 6,
};
static const ValueCase VALUE_NOT_SET = ValueCase::kUndefined;
inline ValueCase value_case() const {
  if (has_tensor_type())
    return ValueCase::kTensorType;
  if (has_sparse_tensor_type())
    return ValueCase::kSparseTensorType;
  if (has_sequence_type())
    return ValueCase::kSequenceType;
  if (has_map_type())
    return ValueCase::kMapType;
  if (has_optional_type())
    return ValueCase::kOptionalType;
  if (has_opaque_type())
    return ValueCase::kOpaqueType;
  return ValueCase::kUndefined;
}
END_PROTO()

// ValueInfoProto

// message ValueInfoProto {
//   optional string name = 1;
//   optional TypeProto type = 2;
//   optional string doc_string = 3;
//   repeated StringStringEntryProto metadata_props = 4;
// }
BEGIN_PROTO(
    ValueInfoProto,
    "Defines information on value, including the name, the type, and the shape of the value.")
FIELD_STR(name, 1, "This field MUST be present in this version of the IR.")
FIELD_OPTIONAL(TypeProto, type, 2,
               "This field MUST be present in this version of the IR for inputs and outputs of "
               "the top-level graph.")
FIELD_STR(doc_string, 3, "A human-readable documentation for this tensor. Markdown is allowed.")
FIELD_REPEATED_PROTO(StringStringEntryProto, metadata_props, 4,
                     "Named metadata values; keys should be distinct.")
END_PROTO()

// AttributeProto
class GraphProto;

// message AttributeProto {
//   reserved 12, 16 to 19;
//   reserved "v";
//   enum AttributeType {
//     UNDEFINED = 0;
//     FLOAT = 1;
//     INT = 2;
//     STRING = 3;
//     TENSOR = 4;
//     GRAPH = 5;
//     SPARSE_TENSOR = 11;
//     TYPE_PROTO = 13;
//     FLOATS = 6;
//     INTS = 7;
//     STRINGS = 8;
//     TENSORS = 9;
//     GRAPHS = 10;
//     SPARSE_TENSORS = 12;
//     TYPE_PROTOS = 14;
//   }
//   optional string name = 1;
//   optional string ref_attr_name = 21;
//   optional string doc_string = 13;
//   optional AttributeType type = 20;
//   optional float f = 2;
//   optional int64 i = 3;
//   optional bytes s = 4;
//   optional TensorProto t = 5;
//   optional GraphProto g = 6;
//   optional SparseTensorProto sparse_tensor = 22;
//   optional TypeProto tp = 14;
//   repeated float floats = 7;
//   repeated int64 ints = 8;
//   repeated bytes strings = 9;
//   repeated TensorProto tensors = 10;
//   repeated GraphProto graphs = 11;
//   repeated SparseTensorProto sparse_tensors = 23;
//   repeated TypeProto type_protos = 15;
// }
BEGIN_PROTO_NOINIT(
    AttributeProto,
    "A named attribute containing either singular float, integer, string, graph, and "
    "tensor values, or repeated float, integer, string, graph, and tensor values. An "
    "AttributeProto MUST contain the name field, and *only one* of the following "
    "content fields, effectively enforcing a C/C++ union equivalent.")
enum AttributeType : int32_t {
  UNDEFINED = 0,
  FLOAT = 1,
  INT = 2,
  STRING = 3,
  TENSOR = 4,
  GRAPH = 5,
  SPARSE_TENSOR = 11,
  TYPE_PROTO = 13,

  FLOATS = 6,
  INTS = 7,
  STRINGS = 8,
  TENSORS = 9,
  GRAPHS = 10,
  SPARSE_TENSORS = 12,
  TYPE_PROTOS = 14,

  // Flat protobuf-style enumerator aliases (drop-in compatibility).
  AttributeProto_AttributeType_UNDEFINED = 0,
  AttributeProto_AttributeType_FLOAT = 1,
  AttributeProto_AttributeType_INT = 2,
  AttributeProto_AttributeType_STRING = 3,
  AttributeProto_AttributeType_TENSOR = 4,
  AttributeProto_AttributeType_GRAPH = 5,
  AttributeProto_AttributeType_SPARSE_TENSOR = 11,
  AttributeProto_AttributeType_TYPE_PROTO = 13,
  AttributeProto_AttributeType_FLOATS = 6,
  AttributeProto_AttributeType_INTS = 7,
  AttributeProto_AttributeType_STRINGS = 8,
  AttributeProto_AttributeType_TENSORS = 9,
  AttributeProto_AttributeType_GRAPHS = 10,
  AttributeProto_AttributeType_SPARSE_TENSORS = 12,
  AttributeProto_AttributeType_TYPE_PROTOS = 14,
};

inline static constexpr const char *AttributeType_Name(AttributeType t) {
  switch (t) {
  case FLOAT:
    return "FLOAT";
  case INT:
    return "INT";
  case STRING:
    return "STRING";
  case TENSOR:
    return "TENSOR";
  case GRAPH:
    return "GRAPH";
  case SPARSE_TENSOR:
    return "SPARSE_TENSOR";
  case TYPE_PROTO:
    return "TYPE_PROTO";
  case FLOATS:
    return "FLOATS";
  case INTS:
    return "INTS";
  case STRINGS:
    return "STRINGS";
  case TENSORS:
    return "TENSORS";
  case GRAPHS:
    return "GRAPHS";
  case SPARSE_TENSORS:
    return "SPARSE_TENSORS";
  case TYPE_PROTOS:
    return "TYPE_PROTOS";
  case UNDEFINED:
  default:
    return "UNDEFINED";
  }
}
/** Overload accepting int for protobuf compatibility. */
inline static constexpr const char *AttributeType_Name(int t) {
  return AttributeType_Name(static_cast<AttributeType>(t));
}

inline AttributeProto() { type_ = AttributeType::UNDEFINED; }

FIELD_STR(name, 1, "Attribute name. This field MUST be present in this version of the IR.")
FIELD_STR(
    ref_attr_name, 21,
    "If ref_attr_name is not empty, ref_attr_name is the attribute name in parent function. In "
    "this case, this AttributeProto does not contain data, and it's a reference of attribute in "
    "parent scope. NOTE: This should ONLY be used in function (sub-graph). It's invalid to be "
    "used in main graph.")
FIELD_STR(doc_string, 13, "A human-readable documentation for this tensor. Markdown is allowed.")
FIELD(
    AttributeType, type, 20,
    "The type field MUST be present for this version of the IR. For 0.0.1 versions of the IR, this "
    "field was not defined, and implementations needed to use has_field heuristics to determine "
    "which value field was in use.  For IR_VERSION 0.0.2 or later, this field MUST be set and "
    "match "
    "the f|i|s|t|... field in use.  This change was made to accommodate proto3 implementations.")
FIELD_OPTIONAL(float, f, 2, "Optional float attribute.")
FIELD_OPTIONAL(int64_t, i, 3, "Optional int64 attribute.")
FIELD_STR(s, 4, "Optional string attribute.")
/** Releases ownership of the string field s (protobuf compat). Caller owns the result. */
inline std::string *release_s() {
  if (!has_s())
    return nullptr;
  auto *p = new std::string(std::move(s_.value()));
  s_.reset();
  return p;
}
FIELD_OPTIONAL(TensorProto, t, 5, "Optional tensor attribute.")
FIELD_OPTIONAL(GraphProto, g, 6, "Optional graph attribute.")
FIELD_OPTIONAL(SparseTensorProto, sparse_tensor, 22, "Optional sparse tensor attribute.")
FIELD_REPEATED(float, floats, 7, "Optional repeated float attribute.")
FIELD_REPEATED(int64_t, ints, 8, "Optional repeated int64 attribute.")
FIELD_REPEATED_STR(utils::String, strings, 9, "Optional repeated string attribute.")
FIELD_REPEATED_PROTO(TensorProto, tensors, 10, "Optional repeated tensor attribute.")
FIELD_REPEATED_PROTO(SparseTensorProto, sparse_tensors, 23, "Optional repeated tensor attribute.")
FIELD_REPEATED_PROTO(GraphProto, graphs, 11, "Optional repeated graph attribute.")
FIELD_OPTIONAL(TypeProto, tp, 14, "Type proto")
FIELD_REPEATED_PROTO(TypeProto, type_protos, 15, "Optional repeated type_proto attribute.")
inline void Clear() {
  this->~AttributeProto();
  new (this) AttributeProto();
}
END_PROTO()

// NodeProto

// message NodeProto {
//   repeated string input = 1;
//   repeated string output = 2;
//   optional string name = 3;
//   optional string op_type = 4;
//   optional string domain = 7;
//   optional string overload = 8;
//   repeated AttributeProto attribute = 5;
//   optional string doc_string = 6;
//   repeated StringStringEntryProto metadata_props = 9;
//   repeated NodeDeviceConfigurationProto device_configurations = 10;
// }
BEGIN_PROTO(NodeProto,
            "Computation graphs are made up of a DAG of nodes, which represent what is "
            "commonly called a 'layer' or 'pipeline stage' in machine learning frameworks. "
            "For example, it can be a node of type 'Conv' that takes in an image, a filter "
            "tensor and a bias tensor, and produces the convolved output.")
FIELD_REPEATED_STR(utils::String, input, 1, "inputs of the node")
FIELD_REPEATED_STR(utils::String, output, 2, "outputs of the node")
FIELD_STR(
    name, 3,
    "An optional identifier for this node in a graph. This field MAY be absent in this version "
    "of the IR.")
FIELD_STR(op_type, 4, "The symbolic identifier of the Operator to execute.")
FIELD_REPEATED_PROTO(AttributeProto, attribute, 5, "Attributes associated with this node.")
FIELD_STR(domain, 7, "The domain of the OperatorSet that specifies the operator named by op_type.")
FIELD_STR(overload, 8, "Overload identifier, used only to map this to a model-local function.")
FIELD_STR(doc_string, 6, "A human-readable documentation for this node. Markdown is allowed.")
FIELD_REPEATED_PROTO(StringStringEntryProto, metadata_props, 9,
                     "Named metadata values; keys should be distinct.")
FIELD_REPEATED_PROTO(NodeDeviceConfigurationProto, device_configurations, 10,
                     "Configuration of multi-device annotations.")
/**
 * Replaces an existing attribute with the same name in place, or appends
 * *attr* to ``attribute`` if no entry with that name exists yet, and returns
 * a reference to the stored attribute.
 */
AttributeProto &set_attribute(const AttributeProto &attr);
/**
 * Sets metadata property *key* to *value*, updating an existing entry with
 * the same key in place, and returns a reference to the stored entry.
 */
StringStringEntryProto &add_metadata(const std::string &key, const std::string &value);
/**
 * Builds a canonical signature that identifies the computation performed by
 * this node: its operator type, normalised domain, resolved inputs and
 * attributes. ``resolved_inputs`` lets callers substitute the node's declared
 * inputs (for example to account for earlier-dropped duplicates), so two nodes
 * computing the same value share the same signature. Attributes are serialised
 * individually and sorted so their declaration order does not affect the
 * signature; non-printable separators keep the fields unambiguous.
 *
 * Returns the signature string.
 */
std::string Signature(const std::vector<std::string> &resolved_inputs) const;
END_PROTO()

// GraphProto

// message GraphProto {
//   repeated NodeProto node = 1;
//   optional string name = 2;
//   repeated TensorProto initializer = 5;
//   repeated SparseTensorProto sparse_initializer = 15;
//   optional string doc_string = 10;
//   repeated ValueInfoProto input = 11;
//   repeated ValueInfoProto output = 12;
//   repeated ValueInfoProto value_info = 13;
//   repeated TensorAnnotation quantization_annotation = 14;
//   repeated StringStringEntryProto metadata_props = 16;
//   reserved 3, 4, 6 to 9;
//   reserved "ir_version", "producer_version", "producer_tag", "domain";
// }
BEGIN_PROTO(
    GraphProto,
    "A graph defines the computational logic of a model and is comprised of a parameterized "
    "list of nodes that form a directed acyclic graph based on their inputs and outputs. This "
    "is the equivalent of the 'network' or 'graph' in many deep learning frameworks.")
FIELD_REPEATED_PROTO(NodeProto, node, 1, "The nodes in the graph, sorted topologically.")
FIELD_STR(name, 2, "The name of the graph.")
FIELD_REPEATED_PROTO(
    TensorProto, initializer, 5,
    "A list of named sparse tensor values, used to specify constant inputs of the graph. "
    "Each initializer (both TensorProto as well SparseTensorProto) MUST have a name. The "
    "name MUST be unique across both initializer and sparse_initializer, but the name MAY "
    "also appear in the input list.")
FIELD_REPEATED_PROTO(
    SparseTensorProto, sparse_initializer, 15,
    "A list of named tensor values, used to specify constant inputs of the graph. Each initializer "
    "(both TensorProto as well SparseTensorProto) MUST have a name. The name MUST be unique across "
    "both initializer and sparse_initializer, but the name MAY also appear in the input list.")
FIELD_STR(doc_string, 10, "A human-readable documentation for this graph. Markdown is allowed.")
FIELD_REPEATED_PROTO(ValueInfoProto, input, 11,
                     "Inputs of the graph, shapes and types are optional in a subgraph and "
                     "mandatory in the main graph.")
FIELD_REPEATED_PROTO(
    ValueInfoProto, output, 12,
    "Outputs of the graph, shapes and types are optional in a subgraph and mandatory in the "
    "main graph.")
FIELD_REPEATED_PROTO(
    ValueInfoProto, value_info, 13,
    "Information for the values in the graph. The ValueInfoProto.name's must be distinct. "
    "It is optional for a value to appear in value_info list.")
FIELD_REPEATED_PROTO(
    TensorAnnotation, quantization_annotation, 14,
    "This field carries information to indicate the mapping among a tensor and its quantization "
    "parameter tensors. For example: For tensor 'a', it may have {'SCALE_TENSOR', 'a_scale'} and "
    "{'ZERO_POINT_TENSOR', 'a_zero_point'} annotated, which means, tensor 'a_scale' and tensor "
    "'a_zero_point' are scale and zero point of tensor 'a' in the model.")
FIELD_REPEATED_PROTO(StringStringEntryProto, metadata_props, 16,
                     "Named metadata values; keys should be distinct.")
/**
 * Appends a new node built from *op_type*, *inputs*, *outputs* and the
 * optional *domain* / *name* to the graph and returns a reference to it.
 * Convenience wrapper around the :ref:`MakeNode` factory.
 */
NodeProto &add_node(const std::string &op_type, const std::vector<std::string> &inputs,
                    const std::vector<std::string> &outputs, const std::string &domain = "",
                    const std::string &name = "");
/**
 * Sets metadata property *key* to *value*, updating an existing entry with
 * the same key in place, and returns a reference to the stored entry.
 */
StringStringEntryProto &add_metadata(const std::string &key, const std::string &value);
END_PROTO()

inline constexpr const char *AttributeProto_AttributeType_Name(AttributeProto::AttributeType t) {
  return AttributeProto::AttributeType_Name(t);
}

// FunctionProto

// message FunctionProto {
//   optional string name = 1;
//   reserved 2;
//   reserved "since_version";
//   reserved 3;
//   reserved "status";
//   repeated string input = 4;
//   repeated string output = 5;
//   repeated string attribute = 6;
//   repeated AttributeProto attribute_proto = 11;
//   repeated NodeProto node = 7;
//   optional string doc_string = 8;
//   repeated OperatorSetIdProto opset_import = 9;
//   optional string domain = 10;
//   optional string overload = 13;
//   repeated ValueInfoProto value_info = 12;
//   repeated StringStringEntryProto metadata_props = 14;
// }
BEGIN_PROTO(FunctionProto,
            "A function defines a sub-operator that can be used in a graph. It is similar to a "
            "function in C/C++ or Python, and can be used to define reusable sub-graphs.")
FIELD_STR(name, 1,
          "The name of the function. This field MUST be present in this version of the IR.")
FIELD_REPEATED_STR(utils::String, input, 4, "input names of the function")
FIELD_REPEATED_STR(utils::String, output, 5, "output names of the function")
FIELD_REPEATED_STR(utils::String, attribute, 6, "attribute names of the function")
FIELD_REPEATED_PROTO(AttributeProto, attribute_proto, 11, "typed attributes")
FIELD_REPEATED_PROTO(NodeProto, node, 7, "The nodes in the graph, sorted topologically.")
FIELD_STR(doc_string, 8, "A human-readable documentation for this graph. Markdown is allowed.")
FIELD_REPEATED_PROTO(
    OperatorSetIdProto, opset_import, 9,
    "The OperatorSets this function body (graph) relies on. All nodes in the function body (graph) "
    "will bind against the operator with the same-domain/same-op_type operator with the HIGHEST "
    "version in the referenced operator sets. This means at most one version can be relied for one "
    "domain. The operator sets imported by FunctionProto should be compatible with the ones "
    "imported "
    "by ModelProto. Example, if same operator set say 'A' is imported by FunctionProto and "
    "ModelProto "
    "then versions for the operator set may be different but, the operator schema returned for "
    "op_type, domain, version combination for both the versions should be same.")
FIELD_STR(domain, 10,
          "The domain which this function belongs to. This is part of the unique-id (domain, name, "
          "overload) of FunctionProtos in a model.")
FIELD_STR(overload, 13,
          "The overload identifier of the function. This is part of the unique-id (domain, name, "
          "overload) of FunctionProtos in a model.")
FIELD_REPEATED_PROTO(
    ValueInfoProto, value_info, 12,
    "Information for the values in the graph. The ValueInfoProto.name's must be distinct. "
    "It is optional for a value to appear in value_info list.")
FIELD_REPEATED_PROTO(StringStringEntryProto, metadata_props, 14,
                     "Named metadata values; keys should be distinct.")
/**
 * Appends a new node built from *op_type*, *inputs*, *outputs* and the
 * optional *domain* / *name* to the function body and returns a reference to
 * it. Convenience wrapper around the :ref:`MakeNode` factory.
 */
NodeProto &add_node(const std::string &op_type, const std::vector<std::string> &inputs,
                    const std::vector<std::string> &outputs, const std::string &domain = "",
                    const std::string &name = "");
/**
 * Appends an opset import ``(domain, version)`` and returns a reference to
 * the newly added entry.
 */
OperatorSetIdProto &add_opset(const std::string &domain, int64_t version);
/**
 * Sets metadata property *key* to *value*, updating an existing entry with
 * the same key in place, and returns a reference to the stored entry.
 */
StringStringEntryProto &add_metadata(const std::string &key, const std::string &value);
END_PROTO()

// ModelProto

// message ModelProto {
//   optional int64 ir_version = 1;
//   repeated OperatorSetIdProto opset_import = 8;
//   optional string producer_name = 2;
//   optional string producer_version = 3;
//   optional string domain = 4;
//   optional int64 model_version = 5;
//   optional string doc_string = 6;
//   optional GraphProto graph = 7;
//   repeated StringStringEntryProto metadata_props = 14;
//   repeated TrainingInfoProto training_info = 20;
//   repeated FunctionProto functions = 25;
//   repeated DeviceConfigurationProto configuration = 26;
// }
BEGIN_PROTO(ModelProto,
            "ModelProto is a top-level file/container format for bundling a ML model and "
            "associating its computation graph with metadata. The semantics of the model "
            "are described by the associated GraphProto's.")
FIELD_OPTIONAL(
    int64_t, ir_version, 1,
    "The version of the IR this model targets. See Version enum above. This field MUST be present.")
FIELD_REPEATED_PROTO(
    OperatorSetIdProto, opset_import, 8,
    "The OperatorSets this model relies on. All ModelProtos MUST have at least one entry that "
    "specifies which version of the ONNX OperatorSet is being imported. All nodes in the "
    "ModelProto's "
    "graph will bind against the operator with the same-domain/same-op_type operator with the "
    "HIGHEST "
    "version in the referenced operator sets.")
FIELD_STR(
    producer_name, 2,
    "The name of the framework or tool used to generate this model. This field SHOULD be present "
    "to indicate which implementation/tool/framework emitted the model.")
FIELD_STR(producer_version, 3,
          "The version of the framework or tool used to generate this model. This field SHOULD be "
          "present to indicate which implementation/tool/framework emitted the model.")
FIELD_STR(
    domain, 4,
    "Domain name of the model. We use reverse domain names as name space indicators. For "
    "example: `company.name`. Together with `model_version` and GraphProto.name, this forms the "
    "unique identity of the graph.")
FIELD_OPTIONAL(int64_t, model_version, 5,
               "The version of the graph encoded. See Version enum below.")
FIELD_STR(doc_string, 6, "A human-readable documentation for this graph. Markdown is allowed.")
FIELD_OPTIONAL(GraphProto, graph, 7,
               "The parameterized graph that is evaluated to execute the model.")
FIELD_REPEATED_PROTO(StringStringEntryProto, metadata_props, 14,
                     "Named metadata values; keys should be distinct.")
// FIELD_REPEATED(TrainingInfoProto, training_info, 20, ",not yet implemented")
FIELD_REPEATED_PROTO(
    FunctionProto, functions, 25,
    "A list of function protos local to the model. The (domain, name, overload) tuple must be "
    "unique "
    "across the function protos in this list. In case of any conflicts the behavior (whether the "
    "model "
    "local functions are given higher priority, or standard operator sets are given higher "
    "priority or "
    "this is treated as error) is defined by the runtimes. The operator sets imported by "
    "FunctionProto "
    "should be compatible with the ones imported by ModelProto and other model local "
    "FunctionProtos. "
    "Example, if same operator set say 'A' is imported by a FunctionProto and ModelProto or by 2 "
    "FunctionProtos then versions for the operator set may be different but, the operator schema "
    "returned for op_type, domain, version combination for both the versions should be same for "
    "every "
    "node in the function body. One FunctionProto can reference other FunctionProto in the model, "
    "however, recursive reference is not allowed.")
FIELD_REPEATED_PROTO(
    DeviceConfigurationProto, configuration, 26,
    "Describes different target configurations for a multi-device use case. A model MAY "
    "describe multiple multi-device configurations for execution.")
/**
 * Appends *function* to the model's ``functions`` field and returns a
 * reference to the stored copy.
 */
FunctionProto &add_function(const FunctionProto &function);
/**
 * Appends an opset import ``(domain, version)`` and returns a reference to
 * the newly added entry.
 */
OperatorSetIdProto &add_opset(const std::string &domain, int64_t version);
/**
 * Sets metadata property *key* to *value*, updating an existing entry with
 * the same key in place, and returns a reference to the stored entry.
 */
StringStringEntryProto &add_metadata(const std::string &key, const std::string &value);
/**
 * Serializes the proto into *out* and external weight payloads into *external_files*.
 * External files are split so each file size is at most *max_external_file_size*.
 * Returns ``false`` when ``SerializeOptions.max_serialized_size_bytes`` is exceeded;
 * in that case both *out* and *external_files* are cleared.
 */
bool SerializeToString(std::string &out,
                       std::unordered_map<std::string, std::string> &external_files,
                       size_t max_external_file_size,
                       const std::string &external_file_prefix = "weights") const;
/**
 * Serializes the proto into *out* and external weight payloads into *external_files*.
 * External files are split so each file size is at most *max_external_file_size*.
 * Returns ``false`` when ``SerializeOptions.max_serialized_size_bytes`` is exceeded;
 * in that case both *out* and *external_files* are cleared.
 */
bool SerializeToString(std::string &out,
                       std::unordered_map<std::string, std::string> &external_files,
                       size_t max_external_file_size, const std::string &external_file_prefix,
                       const SerializeOptions &opts) const;
END_PROTO()

// SequenceProto

class MapProto;
class OptionalProto;

// message SequenceProto {
//   optional string name = 1;
//   enum DataType {
//     UNDEFINED = 0;
//     TENSOR = 1;
//     SPARSE_TENSOR = 2;
//     SEQUENCE = 3;
//     MAP = 4;
//     OPTIONAL = 5;
//   }
//   optional int32 elem_type = 2;
//   repeated TensorProto tensor_values = 3;
//   repeated SparseTensorProto sparse_tensor_values = 4;
//   repeated SequenceProto sequence_values = 5;
//   repeated MapProto map_values = 6;
//   repeated OptionalProto optional_values = 7;
// }
BEGIN_PROTO_NOINIT(
    SequenceProto,
    "Defines a dense, ordered, collection of elements that are of homogeneous types. "
    "Sequences can be made out of tensors, maps, or sequences. "
    "If a sequence is made out of tensors, the tensors must have the same element "
    "type (i.e. int32). In some cases, the tensors in a sequence can have different "
    "shapes.  Whether the tensors can have different shapes or not depends on the "
    "type/shape associated with the corresponding `ValueInfo`. For example, "
    "`Sequence<Tensor<float, [M,N]>` means that all tensors have same shape. However, "
    "`Sequence<Tensor<float, [omitted,omitted]>` means they can have different "
    "shapes (all of rank 2), where `omitted` means the corresponding dimension has "
    "no symbolic/constant value. Finally, `Sequence<Tensor<float, omitted>>` means "
    "that the different tensors can have different ranks, when the `shape` itself "
    "is omitted from the tensor-type. For a more complete description")
enum DataType : int32_t {
  UNDEFINED = 0,
  TENSOR = 1,
  SPARSE_TENSOR = 2,
  SEQUENCE = 3,
  MAP = 4,
  OPTIONAL = 5
};
FIELD_STR(name, 1, "An optional identifier for this sequence.");
FIELD(DataType, elem_type, 2,
      "The type of the elements in the sequence. The type of each element MUST match the elem_type "
      "specified. This field MUST be present for this version of the IR.")
FIELD_REPEATED_PROTO(TensorProto, tensor_values, 3,
                     "For TensorProto values. "
                     "When this field is present, the elem_type field MUST be TENSOR. ")
FIELD_REPEATED_PROTO(SparseTensorProto, sparse_tensor_values, 4,
                     "For SparseTensorProto values. "
                     "When this field is present, the elem_type field MUST be SPARSE_TENSOR. ")
FIELD_REPEATED_PROTO(SequenceProto, sequence_values, 5,
                     "For SequenceProto values. "
                     "When this field is present, the elem_type field MUST be SEQUENCE. ")
FIELD_REPEATED_PROTO(MapProto, map_values, 6,
                     "For MapProto values. "
                     "When this field is present, the elem_type field MUST be MAP. ")
FIELD_REPEATED_PROTO(OptionalProto, optional_values, 7,
                     "For Optional values. "
                     "When this field is present, the elem_type field MUST be OPTIONAL. ")
inline SequenceProto() { elem_type_ = DataType::UNDEFINED; }
inline void set_elem_type(int t) { set_elem_type(static_cast<DataType>(t)); }
END_PROTO()

// MapProto

// message MapProto {
//   optional string name = 1;
//   optional int32 key_type = 2;
//   repeated int64 keys = 3;
//   repeated bytes string_keys = 4;
//   optional SequenceProto values = 5;
// }
BEGIN_PROTO_NOINIT(
    MapProto,
    "Specifies an associative table, defined by keys and values. MapProto is formed with a "
    "repeated field of keys (of type INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, "
    "UINT64, or STRING) and values (of type TENSOR, SPARSE_TENSOR, SEQUENCE, or MAP). Key "
    "types and value types have to remain the same throughout the instantiation of the "
    "MapProto.")

FIELD_STR(name, 1, "An optional identifier for this map.")
FIELD(TensorProto::DataType, key_type, 2,
      "The data type of the key. "
      "This field MUST have a valid TensorProto.DataType value of "
      "INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, or STRING")
FIELD_REPEATED(int64_t, keys, 3,
               "Every element of keys has to be one of the following data types "
               "INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, or STRING. "
               "The integer cases are represented by the repeated int64 field keys below.")
FIELD_REPEATED(utils::String, string_keys, 4,
               "If keys are strings, they are represented by the repeated bytes field "
               "string_keys below.")
FIELD(SequenceProto, values, 5,
      "MapProto values are represented in a SequenceProto of the same length as the "
      "repeated keys field and have to be one of the following data types "
      "TENSOR, SPARSE_TENSOR, MAP, SEQUENCE.")
inline MapProto() { key_type_ = TensorProto::DataType::UNDEFINED; }
inline void set_key_type(int t) { set_key_type(static_cast<TensorProto::DataType>(t)); }
END_PROTO()

// OptionalProto

// message OptionalProto {
//   optional string name = 1;
//   enum DataType {
//     UNDEFINED = 0;
//     TENSOR = 1;
//     SPARSE_TENSOR = 2;
//     SEQUENCE = 3;
//     MAP = 4;
//     OPTIONAL = 5;
//   }
//   optional int32 elem_type = 2;
//   optional TensorProto tensor_value = 3;
//   optional SparseTensorProto sparse_tensor_value = 4;
//   optional SequenceProto sequence_value = 5;
//   optional MapProto map_value = 6;
//   optional OptionalProto optional_value = 7;
// }
BEGIN_PROTO_NOINIT(OptionalProto,
                   "A container that may or may not hold a value. The value, if present, may be a "
                   "Tensor, Sparse Tensor, Sequence, Map, or another Optional. An absent value is "
                   "semantically different from a present value that contains an empty tensor, "
                   "sequence, or map. For example, an absent optional tensor means the absence of "
                   "the tensor itself, whereas a present optional tensor that contains an empty "
                   "tensor means the tensor is present but it has no elements.")
enum DataType : int32_t {
  UNDEFINED = 0,
  TENSOR = 1,
  SPARSE_TENSOR = 2,
  SEQUENCE = 3,
  MAP = 4,
  OPTIONAL = 5,
  // Flat protobuf-style enumerator aliases (drop-in compatibility).
  OptionalProto_DataType_UNDEFINED = 0,
  OptionalProto_DataType_TENSOR = 1,
  OptionalProto_DataType_SPARSE_TENSOR = 2,
  OptionalProto_DataType_SEQUENCE = 3,
  OptionalProto_DataType_MAP = 4,
  OptionalProto_DataType_OPTIONAL = 5
};
FIELD_STR(name, 1, "An optional identifier for this optional.");
FIELD(DataType, elem_type, 2,
      "The data type of the element, identifies if the OptionalProto value is Tensor, Sparse "
      "Tensor, Sequence, Map, or Optional. The type of the optional value MUST match the "
      "elem_type specified. This field MUST have a valid OptionalProto.DataType value.")
FIELD_OPTIONAL_ONEOF(TensorProto, tensor_value, 3, value,
                     "For TensorProto value. "
                     "When this field is present, the elem_type field MUST be TENSOR.")
FIELD_OPTIONAL_ONEOF(SparseTensorProto, sparse_tensor_value, 4, value,
                     "For SparseTensorProto value. "
                     "When this field is present, the elem_type field MUST be SPARSE_TENSOR.")
FIELD_OPTIONAL_ONEOF(
    SequenceProto, sequence_value, 5, value,
    "For SequenceProto value. When this field is present, the elem_type field MUST be SEQUENCE.")
FIELD_OPTIONAL_ONEOF(MapProto, map_value, 6, value,
                     "For MapProto value. "
                     "When this field is present, the elem_type field MUST be MAP.")
FIELD_OPTIONAL_ONEOF(OptionalProto, optional_value, 7, value,
                     "For OptionalProto value, allowing optional to be of itself (completeness) "
                     "When this field is present, the elem_type field MUST be OPTIONAL.")
inline OptionalProto() { elem_type_ = DataType::UNDEFINED; }
inline bool has_value() const {
  return has_tensor_value() || has_sequence_value() || has_map_value() ||
         has_sparse_tensor_value() || has_optional_value();
}
inline void set_elem_type(int t) { set_elem_type(static_cast<DataType>(t)); }
END_PROTO()

// Protobuf-style flattened enum constants for SequenceProto::DataType.
inline constexpr auto SequenceProto_DataType_UNDEFINED = SequenceProto::DataType::UNDEFINED;
inline constexpr auto SequenceProto_DataType_TENSOR = SequenceProto::DataType::TENSOR;
inline constexpr auto SequenceProto_DataType_SPARSE_TENSOR = SequenceProto::DataType::SPARSE_TENSOR;
inline constexpr auto SequenceProto_DataType_SEQUENCE = SequenceProto::DataType::SEQUENCE;
inline constexpr auto SequenceProto_DataType_MAP = SequenceProto::DataType::MAP;
inline constexpr auto SequenceProto_DataType_OPTIONAL = SequenceProto::DataType::OPTIONAL;

// Protobuf-style type alias for OptionalProto::DataType.
using OptionalProto_DataType = OptionalProto::DataType;

} // namespace ONNX_LIGHT_NAMESPACE

#include "fields.hpp"
#include "stream_class_adapters.hpp"

#if defined(FIELD)
#undef FIELD
#undef FIELD_REPEATED
#endif
