// Copyright(c) 2006 to 2022 ZettaScale Technology and others
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
// v. 1.0 which is available at
// http://www.eclipse.org/org/documents/edl-v10.php.
//
// SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause

#ifndef DDS_CDRSTREAM_H
#define DDS_CDRSTREAM_H

#include "dds/ddsrt/bswap.h"
#include "dds/ddsrt/static_assert.h"
#include "dds/ddsc/dds_data_type_properties.h"
#include "dds/dds.h"

#if defined (__cplusplus)
extern "C" {
#endif

#define DDS_CDRSTREAM_MAX_NESTING_DEPTH 32  /* maximum level of nesting for key extraction */

/*
  Encoding version to be used for serialization. Encoding version 1
  represents the XCDR1 format as defined in the DDS XTypes specification,
  with PLAIN_CDR(1) that is backwards compatible with the CDR encoding
  used by non-XTypes enabled nodes.
*/
#define DDSI_RTPS_CDR_ENC_VERSION_UNDEF     0
#define DDSI_RTPS_CDR_ENC_VERSION_1         1
#define DDSI_RTPS_CDR_ENC_VERSION_2         2

#define DDSI_RTPS_CDR_ENC_FORMAT_PLAIN      0
#define DDSI_RTPS_CDR_ENC_FORMAT_DELIMITED  1
#define DDSI_RTPS_CDR_ENC_FORMAT_PL         2

/* X-Types spec 7.6.3.1.2: Implementations of this specification shall set the
least significant two bits in the second byte of the options field to a value
that encodes the number of padding bytes needed after the end of the serialized
payload in order to reach the next 4-byte aligned offset. */
#define DDS_CDR_HDR_PADDING_MASK 0x3


#define DDS_CDR_CALCULATED_FLAGS (DDS_TOPIC_FIXED_KEY | DDS_TOPIC_FIXED_KEY_XCDR2 | DDS_TOPIC_FIXED_KEY_XCDR2_KEYHASH | DDS_TOPIC_KEY_APPENDABLE | DDS_TOPIC_KEY_MUTABLE | DDS_TOPIC_KEY_SEQUENCE | DDS_TOPIC_KEY_ARRAY_NONPRIM)

struct dds_cdr_header {
  unsigned short identifier;
  unsigned short options;
};

enum dds_cdr_type_extensibility
{
  DDS_CDR_TYPE_EXT_FINAL = 0,
  DDS_CDR_TYPE_EXT_APPENDABLE = 1,
  DDS_CDR_TYPE_EXT_MUTABLE = 2
};

enum dds_cdr_key_serialization_kind
{
  DDS_CDR_KEY_SERIALIZATION_SAMPLE,
  DDS_CDR_KEY_SERIALIZATION_KEYHASH
};

typedef struct dds_istream {
  const unsigned char *m_buffer;
  uint32_t m_size;          /* Buffer size */
  uint32_t m_index;         /* Read/write offset from start of buffer */
  uint32_t m_xcdr_version;  /* XCDR version of the data */
} dds_istream_t;

typedef struct dds_ostream {
  unsigned char *m_buffer;
  uint32_t m_size;          /* Buffer size */
  uint32_t m_index;         /* Read/write offset from start of buffer */
  uint32_t m_xcdr_version;  /* XCDR version to use for serializing data */
} dds_ostream_t;

typedef struct dds_ostreamBE {
  dds_ostream_t x;
} dds_ostreamBE_t;

typedef struct dds_ostreamLE {
  dds_ostream_t x;
} dds_ostreamLE_t;

typedef struct dds_cdrstream_allocator {
  void* (*malloc) (size_t size);
  void* (*realloc) (void *ptr, size_t new_size);
  void (*free) (void *pt);
  /* In a future version, a void ptr may be needed here as a parameter for
     custom allocator implementations. */
} dds_cdrstream_allocator_t;

typedef struct dds_cdrstream_desc_key {
  uint32_t ops_offs;   /* Offset for key ops */
  uint32_t idx;        /* Key index in containing type (definition order) */
} dds_cdrstream_desc_key_t;

typedef struct dds_cdrstream_desc_keys {
  uint32_t nkeys;
  struct dds_cdrstream_desc_key *keys; // keys in member-id order
  struct dds_cdrstream_desc_key *keys_definition_order;
} dds_cdrstream_desc_keys_t;

typedef struct dds_cdrstream_desc_op_seq {
  uint32_t nops;    /* Number of words in ops (which >= number of ops stored in preproc output) */
  uint32_t *ops;    /* Marshalling meta data */
} dds_cdrstream_desc_op_seq_t;

struct dds_cdrstream_desc {
  uint32_t size;    /* Size of type */
  uint32_t align;   /* Alignment of top-level type */
  uint32_t flagset; /* Flags */
  struct dds_cdrstream_desc_keys keys;
  dds_cdrstream_desc_op_seq_t ops;
  size_t opt_size_xcdr1;
  size_t opt_size_xcdr2;
};


DDSRT_STATIC_ASSERT (offsetof (dds_ostreamLE_t, x) == 0);
DDSRT_STATIC_ASSERT (offsetof (dds_ostreamBE_t, x) == 0);

/** @component cdr_serializer */
uint32_t dds_cdr_alignto4_clear_and_resize (dds_ostream_t *os, const struct dds_cdrstream_allocator *allocator, uint32_t xcdr_version);

/** @component cdr_serializer */
DDS_EXPORT void dds_istream_init (dds_istream_t *is, uint32_t size, const void *input, uint32_t xcdr_version);

/** @component cdr_serializer */
DDS_EXPORT void dds_istream_fini (dds_istream_t *is);

/** @component cdr_serializer */
DDS_EXPORT void dds_ostream_init (dds_ostream_t *os, const struct dds_cdrstream_allocator *allocator, uint32_t size, uint32_t xcdr_version);

/** @component cdr_serializer */
DDS_EXPORT void dds_ostream_fini (dds_ostream_t *os, const struct dds_cdrstream_allocator *allocator);

/** @component cdr_serializer */
DDS_EXPORT void dds_ostreamLE_init (dds_ostreamLE_t *os, const struct dds_cdrstream_allocator *allocator, uint32_t size, uint32_t xcdr_version);

/** @component cdr_serializer */
DDS_EXPORT void dds_ostreamLE_fini (dds_ostreamLE_t *os, const struct dds_cdrstream_allocator *allocator);

/** @component cdr_serializer */
DDS_EXPORT void dds_ostreamBE_init (dds_ostreamBE_t *os, const struct dds_cdrstream_allocator *allocator, uint32_t size, uint32_t xcdr_version);

/** @component cdr_serializer */
DDS_EXPORT void dds_ostreamBE_fini (dds_ostreamBE_t *os, const struct dds_cdrstream_allocator *allocator);

/** @component cdr_serializer */
dds_ostream_t dds_ostream_from_buffer(void *buffer, size_t size, uint16_t write_encoding_version);

/**
 * @brief Normalized and validates CDR data
 * @component cdr_serializer
 *
 * @param data          data sample
 * @param size          size of the data
 * @param bswap         byte-swapping required
 * @param xcdr_version  XCDR version of the CDR data
 * @param desc          type descriptor
 * @param just_key      indicates if the data is a serialized key or a complete sample
 * @param actual_size   is set to the actual size of the data (*actual_size <= size) on successful return
 * @returns             True iff validation and normalization succeeded
 */
DDS_EXPORT bool dds_stream_normalize (void *data, uint32_t size, bool bswap, uint32_t xcdr_version, const struct dds_cdrstream_desc *desc, bool just_key, uint32_t *actual_size)
  ddsrt_attribute_warn_unused_result ddsrt_nonnull_all;

/** @component cdr_serializer */
DDS_EXPORT const uint32_t *dds_stream_normalize_data (char *data, uint32_t *off, uint32_t size, bool bswap, uint32_t xcdr_version, const uint32_t *ops)
  ddsrt_attribute_warn_unused_result ddsrt_nonnull_all;

/** @component cdr_serializer */
DDS_EXPORT const uint32_t *dds_stream_write (dds_ostream_t *os, const struct dds_cdrstream_allocator *allocator, const char *data, const uint32_t *ops)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT const uint32_t *dds_stream_writeLE (dds_ostreamLE_t *os, const struct dds_cdrstream_allocator *allocator, const char *data, const uint32_t *ops)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT const uint32_t *dds_stream_writeBE (dds_ostreamBE_t *os, const struct dds_cdrstream_allocator *allocator, const char *data, const uint32_t *ops)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT const uint32_t * dds_stream_write_with_byte_order (dds_ostream_t *os, const struct dds_cdrstream_allocator *allocator, const char *data, const uint32_t *ops, enum ddsrt_byte_order_selector bo)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT bool dds_stream_write_sample (dds_ostream_t *os, const struct dds_cdrstream_allocator *allocator, const void *data, const struct dds_cdrstream_desc *desc)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT bool dds_stream_write_sampleLE (dds_ostreamLE_t *os, const struct dds_cdrstream_allocator *allocator, const void *data, const struct dds_cdrstream_desc *desc)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT bool dds_stream_write_sampleBE (dds_ostreamBE_t *os, const struct dds_cdrstream_allocator *allocator, const void *data, const struct dds_cdrstream_desc *desc)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT void dds_stream_read_sample (dds_istream_t *is, void *data, const struct dds_cdrstream_allocator *allocator, const struct dds_cdrstream_desc *desc);

/** @component cdr_serializer */
DDS_EXPORT void dds_stream_free_sample (void *data, const struct dds_cdrstream_allocator *allocator, const uint32_t *ops);

/** @component cdr_serializer */
DDS_EXPORT uint32_t dds_stream_countops (const uint32_t *ops, uint32_t nkeys, const dds_key_descriptor_t *keys);

/** @component cdr_serializer */
size_t dds_stream_check_optimize (const struct dds_cdrstream_desc *desc, uint32_t xcdr_version);

/** @component cdr_serializer */
bool dds_stream_write_key (dds_ostream_t *os, enum dds_cdr_key_serialization_kind ser_kind, const struct dds_cdrstream_allocator *allocator, const char *sample, const struct dds_cdrstream_desc *desc)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
bool dds_stream_write_keyBE (dds_ostreamBE_t *os, enum dds_cdr_key_serialization_kind ser_kind, const struct dds_cdrstream_allocator *allocator, const char *sample, const struct dds_cdrstream_desc *desc)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT bool dds_stream_extract_key_from_data (dds_istream_t *is, dds_ostream_t *os, const struct dds_cdrstream_allocator *allocator, const struct dds_cdrstream_desc *desc)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT void dds_stream_extract_key_from_key (dds_istream_t *is, dds_ostream_t *os, enum dds_cdr_key_serialization_kind ser_kind, const struct dds_cdrstream_allocator *allocator, const struct dds_cdrstream_desc *desc);

/** @component cdr_serializer */
DDS_EXPORT bool dds_stream_extract_keyBE_from_data (dds_istream_t *is, dds_ostreamBE_t *os, const struct dds_cdrstream_allocator *allocator, const struct dds_cdrstream_desc *desc)
  ddsrt_attribute_warn_unused_result;

/** @component cdr_serializer */
DDS_EXPORT void dds_stream_extract_keyBE_from_key (dds_istream_t *is, dds_ostreamBE_t *os, enum dds_cdr_key_serialization_kind ser_kind, const struct dds_cdrstream_allocator *allocator, const struct dds_cdrstream_desc *desc);

/** @component cdr_serializer */
DDS_EXPORT const uint32_t *dds_stream_read (dds_istream_t *is, char *data, const struct dds_cdrstream_allocator *allocator, const uint32_t *ops);

/** @component cdr_serializer */
DDS_EXPORT void dds_stream_read_key (dds_istream_t *is, char *sample, const struct dds_cdrstream_allocator *allocator, const struct dds_cdrstream_desc *desc);

/** @component cdr_serializer */
DDS_EXPORT size_t dds_stream_print_key (dds_istream_t *is, const struct dds_cdrstream_desc *desc, char *buf, size_t size);

/** @component cdr_serializer */
DDS_EXPORT size_t dds_stream_print_sample (dds_istream_t *is, const struct dds_cdrstream_desc *desc, char *buf, size_t size);

/** @component cdr_serializer */
DDS_EXPORT size_t dds_stream_getsize_sample (const char *data, const struct dds_cdrstream_desc *desc, uint32_t xcdr_version)
  ddsrt_nonnull_all;

/** @component cdr_serializer */
DDS_EXPORT size_t dds_stream_getsize_key (const char *sample, const struct dds_cdrstream_desc *desc, uint32_t xcdr_version)
  ddsrt_nonnull_all;

/** @component cdr_serializer */
uint16_t dds_stream_minimum_xcdr_version (const uint32_t *ops);

/** @component cdr_serializer */
uint32_t dds_stream_type_nesting_depth (const uint32_t *ops);

/** @component cdr_serializer */
uint32_t dds_stream_key_flags (struct dds_cdrstream_desc *desc, uint32_t *keysz_xcdrv1, uint32_t *keysz_xcdrv2);

/** @component cdr_serializer */
bool dds_stream_extensibility (const uint32_t *ops, enum dds_cdr_type_extensibility *ext);

/** @component cdr_serializer */
dds_data_type_properties_t dds_stream_data_types (const uint32_t *ops);

/** @component cdr_serializer */
DDS_EXPORT void dds_cdrstream_desc_init (struct dds_cdrstream_desc *desc, const struct dds_cdrstream_allocator *allocator,
    uint32_t size, uint32_t align, uint32_t flagset, const uint32_t *ops, const dds_key_descriptor_t *keys, uint32_t nkeys);

/** @component cdr_serializer */
DDS_EXPORT void dds_cdrstream_desc_fini (struct dds_cdrstream_desc *desc, const struct dds_cdrstream_allocator *allocator);

/** @component cdr_serializer */
DDS_EXPORT void dds_cdrstream_desc_from_topic_desc (struct dds_cdrstream_desc *desc, const dds_topic_descriptor_t *topic_desc);


#if defined (__cplusplus)
}
#endif
#endif
