#include <mongoc/mcd-rpc.h>

#include <mlib/intencode.h>

// Header-only dependency. Does NOT require linking with libmongoc.
#define MONGOC_INSIDE
#include <mongoc/mongoc-iovec.h>
#undef MONGOC_INSIDE

#include <bson/bson.h>

#include <mlib/cmp.h>


typedef struct _mcd_rpc_message_header mcd_rpc_message_header;

typedef struct _mcd_rpc_op_compressed mcd_rpc_op_compressed;

typedef struct _mcd_rpc_op_msg_section mcd_rpc_op_msg_section;
typedef struct _mcd_rpc_op_msg mcd_rpc_op_msg;

typedef struct _mcd_rpc_op_reply mcd_rpc_op_reply;
typedef struct _mcd_rpc_op_update mcd_rpc_op_update;
typedef struct _mcd_rpc_op_insert mcd_rpc_op_insert;
typedef struct _mcd_rpc_op_query mcd_rpc_op_query;
typedef struct _mcd_rpc_op_get_more mcd_rpc_op_get_more;
typedef struct _mcd_rpc_op_delete mcd_rpc_op_delete;
typedef struct _mcd_rpc_op_kill_cursors mcd_rpc_op_kill_cursors;


// See: https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol
struct _mcd_rpc_message_header {
   int32_t message_length;
   int32_t request_id;
   int32_t response_to;
   int32_t op_code;
   bool is_in_iovecs_state; // Not part of actual message.
};

struct _mcd_rpc_op_compressed {
   mcd_rpc_message_header header;
   int32_t original_opcode;
   int32_t uncompressed_size;
   uint8_t compressor_id;
   const void *compressed_message; // Non-owning.
   size_t compressed_message_len;  // Not part of actual message.
};

struct _mcd_rpc_op_msg_section {
   uint8_t kind;

   union payload {
      // Kind 0.
      struct body {
         int32_t section_len; // Not part of actual message.
         const void *bson;    // bson_t data, non-owning.
      } body;

      // Kind 1.
      struct document_sequence {
         int32_t section_len;
         const char *identifier;   // Non-owning.
         size_t identifier_len;    // Not part of actual message.
         const void *bson_objects; // Array of bson_t data, non-owning.
         size_t bson_objects_len;  // Not part of actual message.
      } document_sequence;
   } payload;
};

struct _mcd_rpc_op_msg {
   mcd_rpc_message_header header;
   uint32_t flag_bits;
   mcd_rpc_op_msg_section *sections; // Owning.
   size_t sections_count;            // Not part of actual message.
   uint32_t checksum;                // Optional, ignored by Drivers.
   bool checksum_set;                // Not part of actual message.
};

struct _mcd_rpc_op_reply {
   mcd_rpc_message_header header;
   int32_t response_flags;
   int64_t cursor_id;
   int32_t starting_from;
   int32_t number_returned;
   const void *documents; // Array of bson_t data, non-owning.
   size_t documents_len;  // Not part of actual message.
};

struct _mcd_rpc_op_update {
   mcd_rpc_message_header header;
   const char *full_collection_name; // Non-owning.
   size_t full_collection_name_len;  // Not part of actual message.
   int32_t flags;
   const void *selector; // bson_t data, non-owning.
   const void *update;   // bson_t data, non-owning.
};

struct _mcd_rpc_op_insert {
   mcd_rpc_message_header header;
   int32_t flags;
   const char *full_collection_name; // Non-owning.
   size_t full_collection_name_len;  // Not part of actual message.
   const void *documents;            // Array of bson_t data, non-owning.
   size_t documents_len;             // Not part of actual message.
};

struct _mcd_rpc_op_query {
   mcd_rpc_message_header header;
   int32_t flags;
   const char *full_collection_name; // Non-owning.
   size_t full_collection_name_len;  // Not part of actual message.
   int32_t number_to_skip;
   int32_t number_to_return;
   const void *query;                  // bson_t, non-owning.
   const void *return_fields_selector; // Optional bson_t, non-owning.
};

struct _mcd_rpc_op_get_more {
   mcd_rpc_message_header header;
   const char *full_collection_name; // Non-owning.
   size_t full_collection_name_len;  // Not part of actual message.
   int32_t number_to_return;
   int64_t cursor_id;
};

struct _mcd_rpc_op_delete {
   mcd_rpc_message_header header;
   const char *full_collection_name;
   size_t full_collection_name_len; // Not part of actual message.
   int32_t flags;
   const void *selector; // bson_t data, non-owning.
};

struct _mcd_rpc_op_kill_cursors {
   mcd_rpc_message_header header;
   int32_t zero; // Reserved.
   int32_t number_of_cursor_ids;
   int64_t *cursor_ids; // Array of int64_t, owning.
};

union _mcd_rpc_message {
   mcd_rpc_message_header msg_header; // Common initial sequence.

   mcd_rpc_op_compressed op_compressed;
   mcd_rpc_op_msg op_msg;

   mcd_rpc_op_reply op_reply;
   mcd_rpc_op_update op_update;
   mcd_rpc_op_insert op_insert;
   mcd_rpc_op_query op_query;
   mcd_rpc_op_get_more op_get_more;
   mcd_rpc_op_delete op_delete;
   mcd_rpc_op_kill_cursors op_kill_cursors;
};


// The minimum byte length of a valid RPC message is 16 bytes (messageHeader is
// the common initial sequence for all opcodes). RPC message lengths less than
// 16 may be encountered parsing due to invalid or malformed input.
#define MONGOC_RPC_MINIMUM_MESSAGE_LENGTH INT32_C(16)

// The minimum byte length of a valid BSON document is 5 bytes (empty document):
//  - length (int32): total document length (including itself).
//  - "\x00": document terminator.
// BSON document lengths less than 5 may be encountered during parsing due to
// invalid or malformed input.
#define MONGOC_RPC_MINIMUM_BSON_LENGTH INT32_C(5)

// To avoid unexpected behavior on big endian targets after
// `mcd_rpc_message_to_iovecs` due to fields being converted to little endian,
// forbid use of accessors unless the RPC message has been reset to an
// initialized state by asserting `!is_in_iovecs_state` even on little endian
// targets.
#define ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS           \
   if (1) {                                             \
      BSON_ASSERT_PARAM(rpc);                           \
      BSON_ASSERT(!rpc->msg_header.is_in_iovecs_state); \
   } else                                               \
      (void)0

// In addition to validating expected size against remaining bytes, ensure
// proper conversion from little endian format.
#define MONGOC_RPC_CONSUME(type, raw_type, from_le)                                        \
   static bool _consume_##type(type *target, const uint8_t **ptr, size_t *remaining_bytes) \
   {                                                                                       \
      BSON_ASSERT_PARAM(target);                                                           \
      BSON_ASSERT_PARAM(ptr);                                                              \
      BSON_ASSERT_PARAM(remaining_bytes);                                                  \
                                                                                           \
      if (*remaining_bytes < sizeof(type)) {                                               \
         return false;                                                                     \
      }                                                                                    \
                                                                                           \
      raw_type raw;                                                                        \
      memcpy(&raw, *ptr, sizeof(type));                                                    \
                                                                                           \
      const raw_type native = from_le(raw);                                                \
      memcpy(target, &native, sizeof(type));                                               \
                                                                                           \
      *ptr += sizeof(type);                                                                \
      *remaining_bytes -= sizeof(type);                                                    \
                                                                                           \
      return true;                                                                         \
   }

MONGOC_RPC_CONSUME(uint8_t, uint8_t, (uint8_t))
MONGOC_RPC_CONSUME(int32_t, uint32_t, BSON_UINT32_FROM_LE)
MONGOC_RPC_CONSUME(uint32_t, uint32_t, BSON_UINT32_FROM_LE)
MONGOC_RPC_CONSUME(int64_t, uint64_t, BSON_UINT64_FROM_LE)

static bool
_consume_utf8(const char **target, size_t *length, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(target);
   BSON_ASSERT_PARAM(length);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   *target = (const char *)*ptr;

   const uint8_t *iter = *ptr;
   size_t rem = *remaining_bytes;

   while (rem > 0u && *iter != '\0') {
      iter += 1u;
      rem -= 1u;
   }

   if (rem == 0u) {
      return false;
   }

   // Consume string including the null terminator.
   iter += 1u;
   rem -= 1u;
   *length = *remaining_bytes - rem;
   *ptr = iter;
   *remaining_bytes = rem;

   return true;
}


static bool
_consume_reserved_zero(const uint8_t **ptr, size_t *remaining_bytes)
{
   int32_t zero;

   if (!_consume_int32_t(&zero, ptr, remaining_bytes)) {
      return false;
   }

   if (zero != 0) {
      *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                               // ZERO as invalid input.
      return false;
   }

   return true;
}


static bool
_consume_bson_objects(const uint8_t **ptr, size_t *remaining_bytes, int32_t *num_parsed, int32_t limit)
{
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);
   BSON_OPTIONAL_PARAM(num_parsed);

   int32_t count = 0;

   // Validate document count and lengths.
   while ((count < limit) && (*remaining_bytes > 0u)) {
      int32_t doc_len;
      if (!_consume_int32_t(&doc_len, ptr, remaining_bytes)) {
         return false;
      }

      if (doc_len < MONGOC_RPC_MINIMUM_BSON_LENGTH || mlib_cmp(doc_len, >, *remaining_bytes + sizeof(int32_t))) {
         *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                                  // document as invalid input.
         return false;
      }

      // Consume rest of document without validation.
      *ptr += (size_t)doc_len - sizeof(int32_t);
      *remaining_bytes -= (size_t)doc_len - sizeof(int32_t);

      count += 1;
   }

   if (num_parsed) {
      *num_parsed = count;
   }

   return true;
}


static bool
_consume_op_compressed(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_compressed *const op_compressed = &rpc->op_compressed;

   if (!_consume_int32_t(&op_compressed->original_opcode, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_compressed->uncompressed_size, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_uint8_t(&op_compressed->compressor_id, ptr, remaining_bytes)) {
      return false;
   }

   // Consume compressedMessage without validation.
   op_compressed->compressed_message = *ptr;
   op_compressed->compressed_message_len = *remaining_bytes;
   *ptr += *remaining_bytes;
   *remaining_bytes = 0u;

   return true;
}


static bool
_consume_op_msg_section(
   mcd_rpc_op_msg *op_msg, const uint8_t **ptr, size_t *remaining_bytes, size_t *capacity, bool *found_kind_0)
{
   BSON_ASSERT_PARAM(op_msg);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(found_kind_0);

   mcd_rpc_op_msg_section section;

   if (!_consume_uint8_t(&section.kind, ptr, remaining_bytes)) {
      return false;
   }

   // There is no ordering implied by payload types. A section with payload type
   // 1 can be serialized before payload type 0.
   if (section.kind == 0) {
      if (*found_kind_0) {
         *ptr -= sizeof(uint8_t); // Revert so *data_end points to start of
                                  // section as invalid input.
         return false;
      }

      *found_kind_0 = true;
   }

   switch (section.kind) {
   case 0: { // Body
      section.payload.body.section_len = mlib_read_i32le(*ptr);
      section.payload.body.bson = *ptr;

      int32_t num_parsed = 0;
      if (!_consume_bson_objects(ptr, remaining_bytes, &num_parsed, 1) || num_parsed != 1) {
         return false;
      }

      break;
   }

   case 1: { // Document Sequence
      if (!_consume_int32_t(&section.payload.document_sequence.section_len, ptr, remaining_bytes)) {
         return false;
      }

      // Minimum byte length of a valid document sequence section is 4 bytes
      // (section length). Actual minimum length would also account for the
      // identifier field, but 4 bytes is sufficient to avoid unsigned integer
      // overflow when computing `remaining_section_bytes` and to encourage as
      // much progress is made parsing input data as able.
      if (mlib_cmp(section.payload.document_sequence.section_len, <, sizeof(int32_t))) {
         *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                                  // document sequence as invalid input.
         return false;
      }

      size_t remaining_section_bytes = (size_t)section.payload.document_sequence.section_len - sizeof(int32_t);

      // Section length exceeds remaining data.
      if (remaining_section_bytes > *remaining_bytes) {
         *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                                  // document sequence as invalid input.
         return false;
      }

      // Consume identifier without validating uniqueness.
      {
         const uint8_t *const identifier_begin = *ptr;
         if (!_consume_utf8(&section.payload.document_sequence.identifier,
                            &section.payload.document_sequence.identifier_len,
                            ptr,
                            &remaining_section_bytes)) {
            return false;
         }
         *remaining_bytes -= (size_t)(*ptr - identifier_begin);
      }

      section.payload.document_sequence.bson_objects = *ptr;
      section.payload.document_sequence.bson_objects_len = remaining_section_bytes;

      _consume_bson_objects(ptr, &remaining_section_bytes, NULL, INT32_MAX);

      // Should have exhausted all bytes in the section.
      if (remaining_section_bytes != 0u) {
         return false;
      }

      *remaining_bytes -= (size_t)(*ptr - (const uint8_t *)section.payload.document_sequence.bson_objects);

      break;
   }

   default:
      *ptr -= sizeof(uint8_t); // Revert so *data_end points to start of kind
                               // as invalid input.
      return false;
   }

   // Expand sections capacity if required.
   if (op_msg->sections_count >= *capacity) {
      *capacity *= 2u;
      op_msg->sections = bson_realloc(op_msg->sections, *capacity * sizeof(mcd_rpc_op_msg_section));
   }

   // Append the valid section.
   op_msg->sections[op_msg->sections_count++] = section;

   return true;
}

static bool
_consume_op_msg(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_msg *const op_msg = &rpc->op_msg;

   if (!_consume_uint32_t(&op_msg->flag_bits, ptr, remaining_bytes)) {
      return false;
   }

   {
      const uint32_t defined_bits =
         MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT | MONGOC_OP_MSG_FLAG_MORE_TO_COME | MONGOC_OP_MSG_FLAG_EXHAUST_ALLOWED;

      // Clients MUST error if any unsupported or undefined required bits are
      // set to 1 and MUST ignore all undefined optional bits.
      if (((op_msg->flag_bits & UINT32_C(0x0000FFFF)) & ~defined_bits) != 0u) {
         *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                                  // flagBits as invalid input.
         return false;
      }
   }

   // Each message contains one or more sections. Preallocate space for two
   // sections, which should cover the most frequent cases.
   size_t capacity = 2u;
   op_msg->sections = bson_malloc(capacity * sizeof(mcd_rpc_op_msg_section));
   op_msg->sections_count = 0u;

   // A fully constructed OP_MSG MUST contain exactly one Payload Type 0, and
   // optionally any number of Payload Type 1 where each identifier MUST be
   // unique per message.
   {
      bool found_kind_0 = false;

      // A section requires at least 5 bytes for kind (1) + length (4).
      while (*remaining_bytes > 4u) {
         if (!_consume_op_msg_section(op_msg, ptr, remaining_bytes, &capacity, &found_kind_0)) {
            return false;
         }
      }

      if (!found_kind_0) {
         return false;
      }
   }

   if ((op_msg->flag_bits & MONGOC_OP_MSG_FLAG_CHECKSUM_PRESENT) != 0u) {
      if (!_consume_uint32_t(&op_msg->checksum, ptr, remaining_bytes)) {
         return false;
      }

      op_msg->checksum_set = true;
   }

   return true;
}


static bool
_consume_op_reply(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_reply *const op_reply = &rpc->op_reply;

   if (!_consume_int32_t(&op_reply->response_flags, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int64_t(&op_reply->cursor_id, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_reply->starting_from, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_reply->number_returned, ptr, remaining_bytes)) {
      return false;
   }

   if (op_reply->number_returned < 0) {
      *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                               // numberReturned as invalid input.
      return false;
   }

   if (op_reply->number_returned > 0) {
      op_reply->documents = *ptr;
      op_reply->documents_len = *remaining_bytes;
   } else {
      op_reply->documents = NULL;
      op_reply->documents_len = 0u;
   }

   int32_t num_parsed = 0;
   if (!_consume_bson_objects(ptr, remaining_bytes, &num_parsed, op_reply->number_returned) ||
       num_parsed != op_reply->number_returned) {
      return false;
   }

   return true;
}


static bool
_consume_op_update(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_update *const op_update = &rpc->op_update;

   if (!_consume_reserved_zero(ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_utf8(&op_update->full_collection_name, &op_update->full_collection_name_len, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_update->flags, ptr, remaining_bytes)) {
      return false;
   }

   // Bits 2-31 are reserved. Must be set to 0.
   if ((op_update->flags & ~(0x00000003)) != 0) {
      *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                               // flags as invalid input.
      return false;
   }

   int32_t num_parsed = 0;

   op_update->selector = *ptr;
   if (!_consume_bson_objects(ptr, remaining_bytes, &num_parsed, 1) || num_parsed != 1) {
      return false;
   }

   op_update->update = *ptr;
   if (!_consume_bson_objects(ptr, remaining_bytes, &num_parsed, 1) || num_parsed != 1) {
      return false;
   }

   return true;
}


static bool
_consume_op_insert(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_insert *const op_insert = &rpc->op_insert;

   if (!_consume_int32_t(&op_insert->flags, ptr, remaining_bytes)) {
      return false;
   }

   // Bits 1-31 are reserved. Must be set to 0.
   if ((op_insert->flags & ~(0x00000001)) != 0) {
      *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                               // flags as invalid input.
      return false;
   }

   if (!_consume_utf8(&op_insert->full_collection_name, &op_insert->full_collection_name_len, ptr, remaining_bytes)) {
      return false;
   }

   op_insert->documents = *ptr;
   op_insert->documents_len = *remaining_bytes;

   int32_t num_parsed = 0;
   if (!_consume_bson_objects(ptr, remaining_bytes, &num_parsed, INT32_MAX) || num_parsed == 0) {
      return false;
   }

   return true;
}


static bool
_consume_op_query(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_query *const op_query = &rpc->op_query;

   if (!_consume_int32_t(&op_query->flags, ptr, remaining_bytes)) {
      return false;
   }

   // Bit 0 is reserved. Must be set to 0.
   if ((op_query->flags & 0x01) != 0) {
      *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                               // flags as invalid input.
      return false;
   }

   // Bits 8-31 are reserved. Must be set to 0.
   if ((op_query->flags & ~(0x0000007F)) != 0) {
      *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                               // flags as invalid input.
      return false;
   }

   if (!_consume_utf8(&op_query->full_collection_name, &op_query->full_collection_name_len, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_query->number_to_skip, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_query->number_to_return, ptr, remaining_bytes)) {
      return false;
   }

   int32_t num_parsed = 0;

   op_query->query = *ptr;
   if (!_consume_bson_objects(ptr, remaining_bytes, &num_parsed, 1) || num_parsed != 1) {
      return false;
   }

   op_query->return_fields_selector = *ptr;
   if (!_consume_bson_objects(ptr, remaining_bytes, &num_parsed, 1)) {
      return false;
   }

   // returnFieldsSelector is optional.
   if (num_parsed == 0) {
      op_query->return_fields_selector = NULL;
   }

   return true;
}


static bool
_consume_op_get_more(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_get_more *const op_get_more = &rpc->op_get_more;

   if (!_consume_reserved_zero(ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_utf8(
          &op_get_more->full_collection_name, &op_get_more->full_collection_name_len, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_get_more->number_to_return, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int64_t(&op_get_more->cursor_id, ptr, remaining_bytes)) {
      return false;
   }

   return true;
}


static bool
_consume_op_delete(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_delete *const op_delete = &rpc->op_delete;

   if (!_consume_reserved_zero(ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_utf8(&op_delete->full_collection_name, &op_delete->full_collection_name_len, ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_delete->flags, ptr, remaining_bytes)) {
      return false;
   }

   // Bits 1-31 are reserved. Must be set to 0.
   if ((op_delete->flags & ~(0x00000001)) != 0) {
      *ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                               // flags as invalid input.
      return false;
   }

   op_delete->selector = *ptr;

   int32_t num_parsed = 0;
   if (!_consume_bson_objects(ptr, remaining_bytes, &num_parsed, 1) || num_parsed != 1) {
      return false;
   }

   return true;
}


static bool
_consume_op_kill_cursors(mcd_rpc_message *rpc, const uint8_t **ptr, size_t *remaining_bytes)
{
   BSON_ASSERT_PARAM(rpc);
   BSON_ASSERT_PARAM(ptr);
   BSON_ASSERT_PARAM(remaining_bytes);

   mcd_rpc_op_kill_cursors *op_kill_cursors = &rpc->op_kill_cursors;

   if (!_consume_reserved_zero(ptr, remaining_bytes)) {
      return false;
   }

   if (!_consume_int32_t(&op_kill_cursors->number_of_cursor_ids, ptr, remaining_bytes)) {
      return false;
   }

   if (op_kill_cursors->number_of_cursor_ids < 0 ||
       // Truncation may (deliberately) leave unparsed bytes that will later
       // trigger validation failure due to unexpected remaining bytes.
       mlib_cmp(op_kill_cursors->number_of_cursor_ids, >, *remaining_bytes / sizeof(int64_t))) {
      *ptr -= sizeof(int32_t); // Revert so *data_len points to start of
                               // numberOfCursorIds as invalid input.
      return false;
   }

   const size_t cursor_ids_length = (size_t)op_kill_cursors->number_of_cursor_ids * sizeof(int64_t);

   bson_free(op_kill_cursors->cursor_ids);

   if (op_kill_cursors->number_of_cursor_ids > 0) {
      op_kill_cursors->cursor_ids = bson_malloc(cursor_ids_length);
      for (int32_t i = 0; i < op_kill_cursors->number_of_cursor_ids; ++i) {
         if (!_consume_int64_t(op_kill_cursors->cursor_ids + i, ptr, remaining_bytes)) {
            return false;
         }
      }
   } else {
      op_kill_cursors->cursor_ids = NULL;
   }

   return true;
}


mcd_rpc_message *
mcd_rpc_message_from_data(const void *data, size_t length, const void **data_end)
{
   BSON_ASSERT_PARAM(data);
   BSON_OPTIONAL_PARAM(data_end);

   mcd_rpc_message *rpc = bson_malloc(sizeof(mcd_rpc_message));
   mcd_rpc_message *ret = NULL;

   *rpc = (mcd_rpc_message){.msg_header = {0}};

   if (!mcd_rpc_message_from_data_in_place(rpc, data, length, data_end)) {
      goto fail;
   }

   ret = rpc;
   rpc = NULL;

fail:
   mcd_rpc_message_destroy(rpc);
   return ret;
}

bool
mcd_rpc_message_from_data_in_place(mcd_rpc_message *rpc, const void *data, size_t length, const void **data_end)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT_PARAM(data);
   BSON_OPTIONAL_PARAM(data_end);

   bool ret = false;

   size_t remaining_bytes = length;

   const uint8_t *ptr = data;

   if (!_consume_int32_t(&rpc->msg_header.message_length, &ptr, &remaining_bytes)) {
      goto fail;
   }

   if (rpc->msg_header.message_length < MONGOC_RPC_MINIMUM_MESSAGE_LENGTH ||
       mlib_cmp(rpc->msg_header.message_length, >, remaining_bytes + sizeof(int32_t))) {
      ptr -= sizeof(int32_t); // Revert so *data_end points to start of
                              // messageLength as invalid input.
      goto fail;
   }

   // Use reported message length as upper bound.
   remaining_bytes = (size_t)rpc->msg_header.message_length - sizeof(int32_t);

   if (!_consume_int32_t(&rpc->msg_header.request_id, &ptr, &remaining_bytes)) {
      goto fail;
   }

   if (!_consume_int32_t(&rpc->msg_header.response_to, &ptr, &remaining_bytes)) {
      goto fail;
   }

   if (!_consume_int32_t(&rpc->msg_header.op_code, &ptr, &remaining_bytes)) {
      goto fail;
   }

   switch (rpc->msg_header.op_code) {
   case MONGOC_OP_CODE_COMPRESSED:
      if (!_consume_op_compressed(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_MSG:
      if (!_consume_op_msg(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_REPLY:
      if (!_consume_op_reply(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_UPDATE:
      if (!_consume_op_update(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_INSERT:
      if (!_consume_op_insert(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_QUERY:
      if (!_consume_op_query(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_GET_MORE:
      if (!_consume_op_get_more(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_DELETE:
      if (!_consume_op_delete(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_KILL_CURSORS:
      if (!_consume_op_kill_cursors(rpc, &ptr, &remaining_bytes)) {
         goto fail;
      }
      break;

   default:
      ptr -= sizeof(int32_t); // Revert so *data_end points to start of opCode
                              // as invalid input.
      goto fail;
   }

   // Number of bytes parsed do not match the reported message length.
   if (remaining_bytes > 0) {
      goto fail;
   }

   ret = true;

fail:
   if (data_end) {
      *data_end = ptr;
   }

   return ret;
}

static void
_append_iovec_reserve_space_for(mongoc_iovec_t **iovecs,
                                size_t *capacity,
                                const mongoc_iovec_t *header_iovecs,
                                size_t additional_capacity)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(header_iovecs);

   // Expect this function to be invoked only once after initializing the
   // `header_iovecs` array.
   BSON_ASSERT(*capacity == 4u);

   *capacity += additional_capacity;
   *iovecs = BSON_ARRAY_ALLOC(*capacity, mongoc_iovec_t);
   memcpy(*iovecs, header_iovecs, 4u * sizeof(mongoc_iovec_t));
}

static bool
_append_iovec(mongoc_iovec_t *iovecs, size_t *capacity, size_t *count, mongoc_iovec_t iovec)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);

   if (!iovec.iov_base || iovec.iov_len == 0u) {
      return false;
   }

   // Expect iovecs array capacity to have been reserved upfront according to
   // the upper bound of potential iovec objects required for the opcode being
   // converted. This is to minimize (re)allocations.
   BSON_ASSERT(*count < *capacity);

   iovecs[*count] = iovec;
   *count += 1u;

   return true;
}

#define MONGOC_RPC_APPEND_IOVEC(type, raw_type, to_le)                                                    \
   static bool _append_iovec_##type(mongoc_iovec_t *iovecs, size_t *capacity, size_t *count, type *value) \
   {                                                                                                      \
      raw_type storage;                                                                                   \
      memcpy(&storage, value, sizeof(raw_type));                                                          \
      storage = to_le(storage);                                                                           \
      memcpy(value, &storage, sizeof(raw_type));                                                          \
      return _append_iovec(iovecs,                                                                        \
                           capacity,                                                                      \
                           count,                                                                         \
                           (mongoc_iovec_t){                                                              \
                              .iov_base = (void *)value,                                                  \
                              .iov_len = sizeof(type),                                                    \
                           });                                                                            \
   }

MONGOC_RPC_APPEND_IOVEC(uint8_t, uint8_t, (uint8_t))
MONGOC_RPC_APPEND_IOVEC(int32_t, uint32_t, BSON_UINT32_TO_LE)
MONGOC_RPC_APPEND_IOVEC(uint32_t, uint32_t, BSON_UINT32_TO_LE)
MONGOC_RPC_APPEND_IOVEC(int64_t, uint64_t, BSON_UINT64_TO_LE)

static bool
_append_iovec_data(mongoc_iovec_t *iovecs, size_t *capacity, size_t *count, const void *data, size_t length)
{
   return _append_iovec(iovecs,
                        capacity,
                        count,
                        (mongoc_iovec_t){
                           .iov_base = (void *)data,
                           .iov_len = length,
                        });
}

static bool
_append_iovec_reserved_zero(mongoc_iovec_t *iovecs, size_t *capacity, size_t *count)
{
   static int32_t zero = 0u;

   return _append_iovec(iovecs,
                        capacity,
                        count,
                        (mongoc_iovec_t){
                           .iov_base = (void *)&zero,
                           .iov_len = sizeof(zero),
                        });
}

static bool
_append_iovec_op_compressed(mongoc_iovec_t **iovecs,
                            size_t *capacity,
                            size_t *count,
                            mcd_rpc_op_compressed *op_compressed,
                            const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(op_compressed);
   BSON_ASSERT_PARAM(header_iovecs);

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 4u);

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_compressed->original_opcode)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_compressed->uncompressed_size)) {
      return false;
   }

   if (!_append_iovec_uint8_t(*iovecs, capacity, count, &op_compressed->compressor_id)) {
      return false;
   }

   if (!_append_iovec_data(
          *iovecs, capacity, count, op_compressed->compressed_message, op_compressed->compressed_message_len)) {
      return false;
   }

   return true;
}

static bool
_count_section_iovecs(const mcd_rpc_op_msg *op_msg, size_t *section_iovecs)
{
   BSON_ASSERT_PARAM(op_msg);
   BSON_ASSERT_PARAM(section_iovecs);

   for (size_t i = 0u; i < op_msg->sections_count; ++i) {
      *section_iovecs += 1u;

      switch (op_msg->sections[i].kind) {
      case 0: // Body.
         *section_iovecs += 1u;
         break;

      case 1: // Document Sequence.
         *section_iovecs += 3u;
         break;

      default:
         return false;
      }
   }

   return true;
}

static bool
_append_iovec_op_msg(mongoc_iovec_t **iovecs,
                     size_t *capacity,
                     size_t *count,
                     mcd_rpc_op_msg *op_msg,
                     const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(op_msg);
   BSON_ASSERT_PARAM(header_iovecs);

   size_t section_iovecs = 0u;
   if (!_count_section_iovecs(op_msg, &section_iovecs)) {
      return false;
   }

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 2u + section_iovecs);

   if (!_append_iovec_uint32_t(*iovecs, capacity, count, &op_msg->flag_bits)) {
      return false;
   }

   for (size_t i = 0u; i < op_msg->sections_count; ++i) {
      mcd_rpc_op_msg_section *section = &op_msg->sections[i];

      if (!section) {
         return false;
      }

      if (!_append_iovec_uint8_t(*iovecs, capacity, count, &section->kind)) {
         return false;
      }

      switch (section->kind) {
      case 0: // Body
         if (!_append_iovec_data(
                *iovecs, capacity, count, section->payload.body.bson, (size_t)section->payload.body.section_len)) {
            return false;
         }
         break;

      case 1: // Document Sequence
         if (!_append_iovec_int32_t(*iovecs, capacity, count, &section->payload.document_sequence.section_len)) {
            return false;
         }

         if (!_append_iovec_data(*iovecs,
                                 capacity,
                                 count,
                                 section->payload.document_sequence.identifier,
                                 section->payload.document_sequence.identifier_len)) {
            return false;
         }

         if (!_append_iovec_data(*iovecs,
                                 capacity,
                                 count,
                                 section->payload.document_sequence.bson_objects,
                                 section->payload.document_sequence.bson_objects_len)) {
            return false;
         }

         break;

      default:
         return false;
      }
   }

   if (op_msg->checksum_set) {
      if (!_append_iovec_uint32_t(*iovecs, capacity, count, &op_msg->checksum)) {
         return false;
      }
   }

   return true;
}

static bool
_append_iovec_op_reply(mongoc_iovec_t **iovecs,
                       size_t *capacity,
                       size_t *count,
                       mcd_rpc_op_reply *op_reply,
                       const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(op_reply);
   BSON_ASSERT_PARAM(header_iovecs);

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 5u);

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_reply->response_flags)) {
      return false;
   }

   if (!_append_iovec_int64_t(*iovecs, capacity, count, &op_reply->cursor_id)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_reply->starting_from)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_reply->number_returned)) {
      return false;
   }

   if (op_reply->number_returned > 0 &&
       !_append_iovec_data(*iovecs, capacity, count, op_reply->documents, op_reply->documents_len)) {
      return false;
   }

   return true;
}

static bool
_append_iovec_op_update(mongoc_iovec_t **iovecs,
                        size_t *capacity,
                        size_t *count,
                        mcd_rpc_op_update *op_update,
                        const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(op_update);
   BSON_ASSERT_PARAM(header_iovecs);

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 5u);

   if (!_append_iovec_reserved_zero(*iovecs, capacity, count)) {
      return false;
   }

   if (!_append_iovec_data(
          *iovecs, capacity, count, op_update->full_collection_name, op_update->full_collection_name_len)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_update->flags)) {
      return false;
   }

   if (!_append_iovec_data(*iovecs, capacity, count, op_update->selector, mlib_read_u32le(op_update->selector))) {
      return false;
   }

   if (!_append_iovec_data(*iovecs, capacity, count, op_update->update, mlib_read_u32le(op_update->update))) {
      return false;
   }

   return true;
}

static bool
_append_iovec_op_insert(mongoc_iovec_t **iovecs,
                        size_t *capacity,
                        size_t *count,
                        mcd_rpc_op_insert *op_insert,
                        const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(op_insert);
   BSON_ASSERT_PARAM(header_iovecs);

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 3u);

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_insert->flags)) {
      return false;
   }

   if (!_append_iovec_data(
          *iovecs, capacity, count, op_insert->full_collection_name, op_insert->full_collection_name_len)) {
      return false;
   }

   if (!_append_iovec_data(*iovecs, capacity, count, op_insert->documents, op_insert->documents_len)) {
      return false;
   }


   return true;
}

static bool
_append_iovec_op_query(mongoc_iovec_t **iovecs,
                       size_t *capacity,
                       size_t *count,
                       mcd_rpc_op_query *op_query,
                       const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(op_query);
   BSON_ASSERT_PARAM(header_iovecs);

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 5u + (size_t)(!!op_query->return_fields_selector));

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_query->flags)) {
      return false;
   }

   if (!_append_iovec_data(
          *iovecs, capacity, count, op_query->full_collection_name, op_query->full_collection_name_len)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_query->number_to_skip)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_query->number_to_return)) {
      return false;
   }

   if (!_append_iovec_data(*iovecs, capacity, count, op_query->query, mlib_read_u32le(op_query->query))) {
      return false;
   }

   if (op_query->return_fields_selector) {
      if (!_append_iovec_data(*iovecs,
                              capacity,
                              count,
                              op_query->return_fields_selector,
                              mlib_read_u32le(op_query->return_fields_selector))) {
         return false;
      }
   }

   return true;
}

static bool
_append_iovec_op_get_more(mongoc_iovec_t **iovecs,
                          size_t *capacity,
                          size_t *count,
                          mcd_rpc_op_get_more *op_get_more,
                          const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(op_get_more);
   BSON_ASSERT_PARAM(header_iovecs);

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 4u);

   if (!_append_iovec_reserved_zero(*iovecs, capacity, count)) {
      return false;
   }

   if (!_append_iovec_data(
          *iovecs, capacity, count, op_get_more->full_collection_name, op_get_more->full_collection_name_len)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_get_more->number_to_return)) {
      return false;
   }

   if (!_append_iovec_int64_t(*iovecs, capacity, count, &op_get_more->cursor_id)) {
      return false;
   }

   return true;
}

static bool
_append_iovec_op_delete(mongoc_iovec_t **iovecs,
                        size_t *capacity,
                        size_t *count,
                        mcd_rpc_op_delete *op_delete,
                        const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(op_delete);
   BSON_ASSERT_PARAM(header_iovecs);

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 4u);

   if (!_append_iovec_reserved_zero(*iovecs, capacity, count)) {
      return false;
   }

   if (!_append_iovec_data(
          *iovecs, capacity, count, op_delete->full_collection_name, op_delete->full_collection_name_len)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_delete->flags)) {
      return false;
   }

   if (!_append_iovec_data(*iovecs, capacity, count, op_delete->selector, mlib_read_u32le(op_delete->selector))) {
      return false;
   }

   return true;
}

static bool
_append_iovec_op_kill_cursors(mongoc_iovec_t **iovecs,
                              size_t *capacity,
                              size_t *count,
                              mcd_rpc_op_kill_cursors *op_kill_cursors,
                              const mongoc_iovec_t *header_iovecs)
{
   BSON_ASSERT_PARAM(iovecs);
   BSON_ASSERT_PARAM(capacity);
   BSON_ASSERT_PARAM(count);
   BSON_ASSERT_PARAM(op_kill_cursors);
   BSON_ASSERT_PARAM(header_iovecs);

   // Store value before conversion to little endian.
   const int32_t number_of_cursor_ids = op_kill_cursors->number_of_cursor_ids;

   _append_iovec_reserve_space_for(iovecs, capacity, header_iovecs, 3u);

   if (!_append_iovec_reserved_zero(*iovecs, capacity, count)) {
      return false;
   }

   if (!_append_iovec_int32_t(*iovecs, capacity, count, &op_kill_cursors->number_of_cursor_ids)) {
      return false;
   }

   // Each cursorID must be converted to little endian.
   for (int32_t i = 0; i < number_of_cursor_ids; ++i) {
      int64_t *const cursor_id = op_kill_cursors->cursor_ids + i;
      uint64_t storage;
      memcpy(&storage, cursor_id, sizeof(int64_t));
      storage = BSON_UINT64_TO_LE(storage);
      memcpy(cursor_id, &storage, sizeof(int64_t));
   }

   if (number_of_cursor_ids > 0 &&
       !_append_iovec_data(
          *iovecs, capacity, count, op_kill_cursors->cursor_ids, (size_t)number_of_cursor_ids * sizeof(int64_t))) {
      return false;
   }

   return true;
}


void *
mcd_rpc_message_to_iovecs(mcd_rpc_message *rpc, size_t *count)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT_PARAM(count);

   const int32_t op_code = rpc->msg_header.op_code;

   // Preallocated space for msgHeader fields.
   mongoc_iovec_t header_iovecs[4];
   size_t capacity = 4u;
   *count = 0u;

   (void)_append_iovec_int32_t(header_iovecs, &capacity, count, &rpc->msg_header.message_length);
   (void)_append_iovec_int32_t(header_iovecs, &capacity, count, &rpc->msg_header.request_id);
   (void)_append_iovec_int32_t(header_iovecs, &capacity, count, &rpc->msg_header.response_to);
   (void)_append_iovec_int32_t(header_iovecs, &capacity, count, &rpc->msg_header.op_code);

   mongoc_iovec_t *iovecs = NULL;
   mongoc_iovec_t *ret = NULL;

   // Fields may be converted to little endian even on failure, so consider the
   // RPC object to be in an iovecs state from this point forward regardless of
   // success or failure.
   rpc->msg_header.is_in_iovecs_state = true;

   switch (op_code) {
   case MONGOC_OP_CODE_COMPRESSED:
      if (!_append_iovec_op_compressed(&iovecs, &capacity, count, &rpc->op_compressed, header_iovecs)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_MSG: {
      if (!_append_iovec_op_msg(&iovecs, &capacity, count, &rpc->op_msg, header_iovecs)) {
         goto fail;
      }
      break;
   }

   case MONGOC_OP_CODE_REPLY:
      if (!_append_iovec_op_reply(&iovecs, &capacity, count, &rpc->op_reply, header_iovecs)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_UPDATE:
      if (!_append_iovec_op_update(&iovecs, &capacity, count, &rpc->op_update, header_iovecs)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_INSERT:
      if (!_append_iovec_op_insert(&iovecs, &capacity, count, &rpc->op_insert, header_iovecs)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_QUERY:
      if (!_append_iovec_op_query(&iovecs, &capacity, count, &rpc->op_query, header_iovecs)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_GET_MORE:
      if (!_append_iovec_op_get_more(&iovecs, &capacity, count, &rpc->op_get_more, header_iovecs)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_DELETE:
      if (!_append_iovec_op_delete(&iovecs, &capacity, count, &rpc->op_delete, header_iovecs)) {
         goto fail;
      }
      break;

   case MONGOC_OP_CODE_KILL_CURSORS:
      if (!_append_iovec_op_kill_cursors(&iovecs, &capacity, count, &rpc->op_kill_cursors, header_iovecs)) {
         goto fail;
      }
      break;

   default:
      goto fail;
   }

   ret = iovecs;
   iovecs = NULL;

fail:
   bson_free(iovecs);

   return ret;
}

mcd_rpc_message *
mcd_rpc_message_new(void)
{
   mcd_rpc_message *const rpc = bson_malloc(sizeof(mcd_rpc_message));
   *rpc = (mcd_rpc_message){.msg_header = {0}};
   return rpc;
}

static int32_t
_mcd_rpc_header_get_op_code_maybe_le(const mcd_rpc_message *rpc)
{
   BSON_ASSERT_PARAM(rpc);

   int32_t op_code = rpc->msg_header.op_code;

   // May already be in native endian.
   switch (op_code) {
   case MONGOC_OP_CODE_COMPRESSED:
   case MONGOC_OP_CODE_MSG:
   case MONGOC_OP_CODE_REPLY:
   case MONGOC_OP_CODE_UPDATE:
   case MONGOC_OP_CODE_INSERT:
   case MONGOC_OP_CODE_QUERY:
   case MONGOC_OP_CODE_GET_MORE:
   case MONGOC_OP_CODE_DELETE:
   case MONGOC_OP_CODE_KILL_CURSORS:
      return op_code;

   default:
      // May be in little endian.
      op_code = mlib_read_i32le(&op_code);

      switch (op_code) {
      case MONGOC_OP_CODE_COMPRESSED:
      case MONGOC_OP_CODE_MSG:
      case MONGOC_OP_CODE_REPLY:
      case MONGOC_OP_CODE_UPDATE:
      case MONGOC_OP_CODE_INSERT:
      case MONGOC_OP_CODE_QUERY:
      case MONGOC_OP_CODE_GET_MORE:
      case MONGOC_OP_CODE_DELETE:
      case MONGOC_OP_CODE_KILL_CURSORS:
         return op_code;

      default:
         // Doesn't seem to have been a valid opCode.
         return rpc->msg_header.op_code;
      }
   }
}

static void
_mcd_rpc_message_free_owners(mcd_rpc_message *rpc)
{
   BSON_ASSERT_PARAM(rpc);

   switch (_mcd_rpc_header_get_op_code_maybe_le(rpc)) {
   case MONGOC_OP_CODE_MSG:
      bson_free(rpc->op_msg.sections);
      rpc->op_msg.sections = NULL;
      return;

   case MONGOC_OP_CODE_KILL_CURSORS:
      bson_free(rpc->op_kill_cursors.cursor_ids);
      rpc->op_kill_cursors.cursor_ids = NULL;
      return;

   case MONGOC_OP_CODE_COMPRESSED:
   case MONGOC_OP_CODE_REPLY:
   case MONGOC_OP_CODE_UPDATE:
   case MONGOC_OP_CODE_INSERT:
   case MONGOC_OP_CODE_QUERY:
   case MONGOC_OP_CODE_GET_MORE:
   case MONGOC_OP_CODE_DELETE:
      return;

   default:
      return;
   }
}

void
mcd_rpc_message_destroy(mcd_rpc_message *rpc)
{
   if (!rpc) {
      return;
   }

   _mcd_rpc_message_free_owners(rpc);

   bson_free((void *)rpc);
}

void
mcd_rpc_message_reset(mcd_rpc_message *rpc)
{
   BSON_ASSERT_PARAM(rpc);

   _mcd_rpc_message_free_owners(rpc);

   *rpc = (mcd_rpc_message){.msg_header = {0}};
}

void
mcd_rpc_message_set_length(mcd_rpc_message *rpc, int32_t value)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->msg_header.message_length = value;
}

int32_t
mcd_rpc_header_get_message_length(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   return rpc->msg_header.message_length;
}

int32_t
mcd_rpc_header_get_request_id(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   return rpc->msg_header.request_id;
}

int32_t
mcd_rpc_header_get_response_to(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   return rpc->msg_header.response_to;
}

int32_t
mcd_rpc_header_get_op_code(const mcd_rpc_message *rpc)
{
   BSON_ASSERT_PARAM(rpc); // Permit read access even if the RPC message object
                           // is in an iovecs state.
   return rpc->msg_header.op_code;
}

int32_t
mcd_rpc_header_set_message_length(mcd_rpc_message *rpc, int32_t message_length)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->msg_header.message_length = message_length;
   return sizeof(message_length);
}

int32_t
mcd_rpc_header_set_request_id(mcd_rpc_message *rpc, int32_t request_id)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->msg_header.request_id = request_id;
   return sizeof(request_id);
}

int32_t
mcd_rpc_header_set_response_to(mcd_rpc_message *rpc, int32_t response_to)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->msg_header.response_to = response_to;
   return sizeof(response_to);
}

int32_t
mcd_rpc_header_set_op_code(mcd_rpc_message *rpc, int32_t op_code)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;

   _mcd_rpc_message_free_owners(rpc);

   rpc->msg_header.op_code = op_code;
   return sizeof(op_code);
}


int32_t
mcd_rpc_op_compressed_get_original_opcode(const mcd_rpc_message *rpc)
{
   BSON_ASSERT_PARAM(rpc); // Permit read access even if the RPC message object
                           // is in an iovecs state.
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   return rpc->op_compressed.original_opcode;
}

int32_t
mcd_rpc_op_compressed_get_uncompressed_size(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   return rpc->op_compressed.uncompressed_size;
}

uint8_t
mcd_rpc_op_compressed_get_compressor_id(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   return rpc->op_compressed.compressor_id;
}

const void *
mcd_rpc_op_compressed_get_compressed_message(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   return rpc->op_compressed.compressed_message;
}

size_t
mcd_rpc_op_compressed_get_compressed_message_length(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   return rpc->op_compressed.compressed_message_len;
}

int32_t
mcd_rpc_op_compressed_set_original_opcode(mcd_rpc_message *rpc, int32_t original_opcode)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   rpc->op_compressed.original_opcode = original_opcode;
   return sizeof(original_opcode);
}

int32_t
mcd_rpc_op_compressed_set_uncompressed_size(mcd_rpc_message *rpc, int32_t uncompressed_size)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   rpc->op_compressed.uncompressed_size = uncompressed_size;
   return sizeof(uncompressed_size);
}

int32_t
mcd_rpc_op_compressed_set_compressor_id(mcd_rpc_message *rpc, uint8_t compressor_id)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   rpc->op_compressed.compressor_id = compressor_id;
   return sizeof(compressor_id);
}

int32_t
mcd_rpc_op_compressed_set_compressed_message(mcd_rpc_message *rpc,
                                             const void *compressed_message,
                                             size_t compressed_message_length)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_COMPRESSED);
   BSON_ASSERT(mlib_in_range(int32_t, compressed_message_length));
   rpc->op_compressed.compressed_message = compressed_message;
   rpc->op_compressed.compressed_message_len = compressed_message_length;
   return (int32_t)compressed_message_length;
}


uint8_t
mcd_rpc_op_msg_section_get_kind(const mcd_rpc_message *rpc, size_t index)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);
   return rpc->op_msg.sections[index].kind;
}

int32_t
mcd_rpc_op_msg_section_get_length(const mcd_rpc_message *rpc, size_t index)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);

   const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index];

   switch (section->kind) {
   case 0: { // Body
      return mlib_read_i32le(section->payload.body.bson);
   }

   case 1: { // Document Sequence
      return section->payload.document_sequence.section_len;
   }

   default:
      BSON_UNREACHABLE("invalid section kind");
   }
}

const char *
mcd_rpc_op_msg_section_get_identifier(const mcd_rpc_message *rpc, size_t index)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);

   const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index];
   BSON_ASSERT(section->kind == 1);
   return section->payload.document_sequence.identifier;
}

const void *
mcd_rpc_op_msg_section_get_body(const mcd_rpc_message *rpc, size_t index)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);

   const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index];
   BSON_ASSERT(section->kind == 0);
   return section->payload.body.bson;
}

const void *
mcd_rpc_op_msg_section_get_document_sequence(const mcd_rpc_message *rpc, size_t index)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);

   const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index];
   BSON_ASSERT(section->kind == 1);
   return section->payload.document_sequence.bson_objects;
}

size_t
mcd_rpc_op_msg_section_get_document_sequence_length(const mcd_rpc_message *rpc, size_t index)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);

   const mcd_rpc_op_msg_section *const section = &rpc->op_msg.sections[index];
   BSON_ASSERT(section->kind == 1);
   return section->payload.document_sequence.bson_objects_len;
}

int32_t
mcd_rpc_op_msg_section_set_kind(mcd_rpc_message *rpc, size_t index, uint8_t kind)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);
   rpc->op_msg.sections[index].kind = kind;
   return sizeof(kind);
}

int32_t
mcd_rpc_op_msg_section_set_length(mcd_rpc_message *rpc, size_t index, int32_t length)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);
   BSON_ASSERT(rpc->op_msg.sections[index].kind == 1);
   rpc->op_msg.sections[index].payload.document_sequence.section_len = length;
   return sizeof(length);
}

int32_t
mcd_rpc_op_msg_section_set_identifier(mcd_rpc_message *rpc, size_t index, const char *identifier)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);
   BSON_ASSERT(rpc->op_msg.sections[index].kind == 1);

   const size_t identifier_len = identifier ? strlen(identifier) + 1u : 0u;

   rpc->op_msg.sections[index].payload.document_sequence.identifier = identifier;
   rpc->op_msg.sections[index].payload.document_sequence.identifier_len = identifier_len;

   BSON_ASSERT(mlib_in_range(int32_t, identifier_len));
   return (int32_t)identifier_len;
}

int32_t
mcd_rpc_op_msg_section_set_body(mcd_rpc_message *rpc, size_t index, const void *body)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);
   BSON_ASSERT(rpc->op_msg.sections[index].kind == 0);

   const int32_t section_len = body ? mlib_read_i32le(body) : 0;

   rpc->op_msg.sections[index].payload.body.bson = body;
   rpc->op_msg.sections[index].payload.body.section_len = section_len;

   return section_len;
}

int32_t
mcd_rpc_op_msg_section_set_document_sequence(mcd_rpc_message *rpc,
                                             size_t index,
                                             const void *document_sequence,
                                             size_t document_sequence_length)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   BSON_ASSERT(index < rpc->op_msg.sections_count);
   BSON_ASSERT(rpc->op_msg.sections[index].kind == 1);

   const size_t bson_objects_len = document_sequence ? document_sequence_length : 0u;

   rpc->op_msg.sections[index].payload.document_sequence.bson_objects = document_sequence;
   rpc->op_msg.sections[index].payload.document_sequence.bson_objects_len = bson_objects_len;

   BSON_ASSERT(mlib_in_range(int32_t, document_sequence_length));
   return (int32_t)bson_objects_len;
}


uint32_t
mcd_rpc_op_msg_get_flag_bits(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   return rpc->op_msg.flag_bits;
}

size_t
mcd_rpc_op_msg_get_sections_count(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   return rpc->op_msg.sections_count;
}

const uint32_t *
mcd_rpc_op_msg_get_checksum(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   return rpc->op_msg.checksum_set ? &rpc->op_msg.checksum : NULL;
}

int32_t
mcd_rpc_op_msg_set_flag_bits(mcd_rpc_message *rpc, uint32_t flag_bits)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   rpc->op_msg.flag_bits = flag_bits;
   return sizeof(flag_bits);
}

void
mcd_rpc_op_msg_set_sections_count(mcd_rpc_message *rpc, size_t section_count)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);

   rpc->op_msg.sections = bson_realloc(rpc->op_msg.sections, section_count * sizeof(mcd_rpc_op_msg_section));
   rpc->op_msg.sections_count = section_count;
}

int32_t
mcd_rpc_op_msg_set_checksum(mcd_rpc_message *rpc, uint32_t checksum)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_MSG);
   rpc->op_msg.checksum = checksum;
   rpc->op_msg.checksum_set = true;
   return sizeof(checksum);
}


int32_t
mcd_rpc_op_reply_get_response_flags(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY);
   return rpc->op_reply.response_flags;
}

int64_t
mcd_rpc_op_reply_get_cursor_id(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY);
   return rpc->op_reply.cursor_id;
}

int32_t
mcd_rpc_op_reply_get_starting_from(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY);
   return rpc->op_reply.starting_from;
}

int32_t
mcd_rpc_op_reply_get_number_returned(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY);
   return rpc->op_reply.number_returned;
}

const void *
mcd_rpc_op_reply_get_documents(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY);
   return rpc->op_reply.documents_len > 0 ? rpc->op_reply.documents : NULL;
}

size_t
mcd_rpc_op_reply_get_documents_len(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_REPLY);
   return rpc->op_reply.documents_len;
}

int32_t
mcd_rpc_op_reply_set_response_flags(mcd_rpc_message *rpc, int32_t response_flags)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->op_reply.response_flags = response_flags;
   return sizeof(response_flags);
}

int32_t
mcd_rpc_op_reply_set_cursor_id(mcd_rpc_message *rpc, int64_t cursor_id)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->op_reply.cursor_id = cursor_id;
   return sizeof(cursor_id);
}

int32_t
mcd_rpc_op_reply_set_starting_from(mcd_rpc_message *rpc, int32_t starting_from)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->op_reply.starting_from = starting_from;
   return sizeof(starting_from);
}

int32_t
mcd_rpc_op_reply_set_number_returned(mcd_rpc_message *rpc, int32_t number_returned)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->op_reply.number_returned = number_returned;
   return sizeof(number_returned);
}

int32_t
mcd_rpc_op_reply_set_documents(mcd_rpc_message *rpc, const void *documents, size_t documents_len)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;

   rpc->op_reply.documents = documents;
   rpc->op_reply.documents_len = documents_len;

   BSON_ASSERT(mlib_in_range(int32_t, documents_len));
   return (int32_t)documents_len;
}


const char *
mcd_rpc_op_update_get_full_collection_name(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE);
   return rpc->op_update.full_collection_name;
}

int32_t
mcd_rpc_op_update_get_flags(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE);
   return rpc->op_update.flags;
}

const void *
mcd_rpc_op_update_get_selector(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE);
   return rpc->op_update.selector;
}

const void *
mcd_rpc_op_update_get_update(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE);
   return rpc->op_update.update;
}

int32_t
mcd_rpc_op_update_set_full_collection_name(mcd_rpc_message *rpc, const char *full_collection_name)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;

   const size_t length = full_collection_name ? strlen(full_collection_name) + 1u : 0u;

   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_UPDATE);
   rpc->op_update.full_collection_name = full_collection_name;
   rpc->op_update.full_collection_name_len = length;

   BSON_ASSERT(mlib_in_range(int32_t, length));
   return (int32_t)length;
}

int32_t
mcd_rpc_op_update_set_flags(mcd_rpc_message *rpc, int32_t flags)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->op_update.flags = flags;
   return sizeof(flags);
}

int32_t
mcd_rpc_op_update_set_selector(mcd_rpc_message *rpc, const void *selector)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->op_update.selector = selector;
   return selector ? mlib_read_i32le(selector) : 0;
}

int32_t
mcd_rpc_op_update_set_update(mcd_rpc_message *rpc, const void *update)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   rpc->op_update.update = update;
   return update ? mlib_read_i32le(update) : 0;
}


int32_t
mcd_rpc_op_insert_get_flags(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT);
   return rpc->op_insert.flags;
}

const char *
mcd_rpc_op_insert_get_full_collection_name(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT);
   return rpc->op_insert.full_collection_name;
}

const void *
mcd_rpc_op_insert_get_documents(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT);
   return rpc->op_insert.documents;
}

size_t
mcd_rpc_op_insert_get_documents_len(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT);
   return rpc->op_insert.documents_len;
}

int32_t
mcd_rpc_op_insert_set_flags(mcd_rpc_message *rpc, int32_t flags)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT);
   rpc->op_insert.flags = flags;
   return sizeof(flags);
}

int32_t
mcd_rpc_op_insert_set_full_collection_name(mcd_rpc_message *rpc, const char *full_collection_name)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT);

   const size_t length = full_collection_name ? strlen(full_collection_name) + 1u : 0u;

   rpc->op_insert.full_collection_name = full_collection_name;
   rpc->op_insert.full_collection_name_len = length;

   BSON_ASSERT(mlib_in_range(int32_t, length));
   return (int32_t)length;
}

int32_t
mcd_rpc_op_insert_set_documents(mcd_rpc_message *rpc, const void *documents, size_t documents_len)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_INSERT);

   rpc->op_insert.documents = documents;
   rpc->op_insert.documents_len = documents_len;

   BSON_ASSERT(mlib_in_range(int32_t, documents_len));
   return (int32_t)documents_len;
}


int32_t
mcd_rpc_op_query_get_flags(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   return rpc->op_query.flags;
}

const char *
mcd_rpc_op_query_get_full_collection_name(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   return rpc->op_query.full_collection_name;
}

int32_t
mcd_rpc_op_query_get_number_to_skip(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   return rpc->op_query.number_to_skip;
}

int32_t
mcd_rpc_op_query_get_number_to_return(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   return rpc->op_query.number_to_return;
}

const void *
mcd_rpc_op_query_get_query(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   return rpc->op_query.query;
}

const void *
mcd_rpc_op_query_get_return_fields_selector(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   return rpc->op_query.return_fields_selector;
}

int32_t
mcd_rpc_op_query_set_flags(mcd_rpc_message *rpc, int32_t flags)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   rpc->op_query.flags = flags;
   return sizeof(flags);
}

int32_t
mcd_rpc_op_query_set_full_collection_name(mcd_rpc_message *rpc, const char *full_collection_name)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);

   const size_t length = full_collection_name ? strlen(full_collection_name) + 1u : 0u;

   rpc->op_query.full_collection_name = full_collection_name;
   rpc->op_query.full_collection_name_len = length;

   BSON_ASSERT(mlib_in_range(int32_t, length));
   return (int32_t)length;
}

int32_t
mcd_rpc_op_query_set_number_to_skip(mcd_rpc_message *rpc, int32_t number_to_skip)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   rpc->op_query.number_to_skip = number_to_skip;
   return sizeof(number_to_skip);
}

int32_t
mcd_rpc_op_query_set_number_to_return(mcd_rpc_message *rpc, int32_t number_to_return)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   rpc->op_query.number_to_return = number_to_return;
   return sizeof(number_to_return);
}

int32_t
mcd_rpc_op_query_set_query(mcd_rpc_message *rpc, const void *query)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   rpc->op_query.query = query;
   return mlib_read_i32le(query);
}

int32_t
mcd_rpc_op_query_set_return_fields_selector(mcd_rpc_message *rpc, const void *return_fields_selector)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_QUERY);
   rpc->op_query.return_fields_selector = return_fields_selector;
   return return_fields_selector ? mlib_read_i32le(return_fields_selector) : 0;
}


const char *
mcd_rpc_op_get_more_get_full_collection_name(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE);
   return rpc->op_get_more.full_collection_name;
}

int32_t
mcd_rpc_op_get_more_get_number_to_return(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE);
   return rpc->op_get_more.number_to_return;
}

int64_t
mcd_rpc_op_get_more_get_cursor_id(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE);
   return rpc->op_get_more.cursor_id;
}

int32_t
mcd_rpc_op_get_more_set_full_collection_name(mcd_rpc_message *rpc, const char *full_collection_name)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE);

   const size_t length = full_collection_name ? strlen(full_collection_name) + 1u : 0u;

   rpc->op_get_more.full_collection_name = full_collection_name;
   rpc->op_get_more.full_collection_name_len = length;

   BSON_ASSERT(mlib_in_range(int32_t, length));
   return (int32_t)length;
}

int32_t
mcd_rpc_op_get_more_set_number_to_return(mcd_rpc_message *rpc, int32_t number_to_return)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE);
   rpc->op_get_more.number_to_return = number_to_return;
   return sizeof(number_to_return);
}

int32_t
mcd_rpc_op_get_more_set_cursor_id(mcd_rpc_message *rpc, int64_t cursor_id)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_GET_MORE);
   rpc->op_get_more.cursor_id = cursor_id;
   return sizeof(cursor_id);
}


const char *
mcd_rpc_op_delete_get_full_collection_name(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE);
   return rpc->op_delete.full_collection_name;
}

int32_t
mcd_rpc_op_delete_get_flags(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE);
   return rpc->op_delete.flags;
}

const void *
mcd_rpc_op_delete_get_selector(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE);
   return rpc->op_delete.selector;
}

int32_t
mcd_rpc_op_delete_set_full_collection_name(mcd_rpc_message *rpc, const char *full_collection_name)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE);

   const size_t length = full_collection_name ? strlen(full_collection_name) + 1u : 0u;

   rpc->op_delete.full_collection_name = full_collection_name;
   rpc->op_delete.full_collection_name_len = length;

   BSON_ASSERT(mlib_in_range(int32_t, length));
   return (int32_t)length;
}

int32_t
mcd_rpc_op_delete_set_flags(mcd_rpc_message *rpc, int32_t flags)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE);
   rpc->op_delete.flags = flags;
   return sizeof(flags);
}

int32_t
mcd_rpc_op_delete_set_selector(mcd_rpc_message *rpc, const void *selector)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_DELETE);
   rpc->op_delete.selector = selector;
   return selector ? mlib_read_i32le(selector) : 0;
}


int32_t
mcd_rpc_op_kill_cursors_get_number_of_cursor_ids(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS);
   return rpc->op_kill_cursors.number_of_cursor_ids;
}

const int64_t *
mcd_rpc_op_kill_cursors_get_cursor_ids(const mcd_rpc_message *rpc)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS);
   return rpc->op_kill_cursors.number_of_cursor_ids > 0 ? rpc->op_kill_cursors.cursor_ids : NULL;
}

int32_t
mcd_rpc_op_kill_cursors_set_cursor_ids(mcd_rpc_message *rpc, const int64_t *cursor_ids, int32_t number_of_cursor_ids)
{
   ASSERT_MCD_RPC_ACCESSOR_PRECONDITIONS;
   BSON_ASSERT(rpc->msg_header.op_code == MONGOC_OP_CODE_KILL_CURSORS);
   BSON_ASSERT(mlib_cmp(number_of_cursor_ids, <, (size_t)INT32_MAX / sizeof(int64_t)));

   const size_t cursor_ids_length = (size_t)number_of_cursor_ids * sizeof(int64_t);

   rpc->op_kill_cursors.number_of_cursor_ids = number_of_cursor_ids;

   bson_free(rpc->op_kill_cursors.cursor_ids);

   if (number_of_cursor_ids > 0) {
      rpc->op_kill_cursors.cursor_ids = bson_malloc(cursor_ids_length);
      memcpy(rpc->op_kill_cursors.cursor_ids, cursor_ids, cursor_ids_length);
   } else {
      rpc->op_kill_cursors.cursor_ids = NULL;
   }

   return (int32_t)sizeof(int32_t) + (int32_t)cursor_ids_length;
}
