#include "ace/Message_Block.h"

#if !defined (__ACE_INLINE__)
#include "ace/Message_Block.inl"
#endif /* __ACE_INLINE__ */

#include "ace/Guard_T.h"
#include "ace/Log_Category.h"
#include "ace/Malloc_Base.h"
#include "ace/OS_NS_string.h"

//#define ACE_ENABLE_TIMEPROBES
#include "ace/Timeprobe.h"

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_ALLOC_HOOK_DEFINE (ACE_Message_Block)

#if defined (ACE_ENABLE_TIMEPROBES)

static const char *ACE_MB_Timeprobe_Description[] =
{
  "Message_Block::init_i - enter",
  "Message_Block::init_i - leave",
  "Message_Block::init_i - db alloc",
  "Message_Block::init_i - db ctor",
  "Data_Block::ctor[1] - enter",
  "Data_Block::ctor[1] - leave",
  "Data_Block::ctor[2] - enter",
  "Data_Block::ctor[2] - leave",
  "Data_Block::clone - enter",
  "Data_Block::clone - leave"
};

enum
{
  ACE_MESSAGE_BLOCK_INIT_I_ENTER = 3000,
  ACE_MESSAGE_BLOCK_INIT_I_LEAVE,
  ACE_MESSAGE_BLOCK_INIT_I_DB_ALLOC,
  ACE_MESSAGE_BLOCK_INIT_I_DB_CTOR,
  ACE_DATA_BLOCK_CTOR1_ENTER,
  ACE_DATA_BLOCK_CTOR1_LEAVE,
  ACE_DATA_BLOCK_CTOR2_ENTER,
  ACE_DATA_BLOCK_CTOR2_LEAVE,
  ACE_DATA_BLOCK_CLONE_ENTER,
  ACE_DATA_BLOCK_CLONE_LEAVE
};


// Setup Timeprobes
ACE_TIMEPROBE_EVENT_DESCRIPTIONS (ACE_MB_Timeprobe_Description,
                                  ACE_MESSAGE_BLOCK_INIT_I_ENTER);

#endif /* ACE_ENABLE_TIMEPROBES */

void
ACE_Message_Block::data_block (ACE_Data_Block *db)
{
  ACE_TRACE ("ACE_Message_Block::data_block");
  if (ACE_BIT_DISABLED (this->flags_,
                        ACE_Message_Block::DONT_DELETE)
      && this->data_block_ != 0)
    this->data_block_->release ();

  this->data_block_ = db;

  // Set the read and write pointers in the <Message_Block> to point
  // to the buffer in the <ACE_Data_Block>.
  this->rd_ptr (this->data_block ()->base ());
  this->wr_ptr (this->data_block ()->base ());
}

int
ACE_Message_Block::copy (const char *buf, size_t n)
{
  ACE_TRACE ("ACE_Message_Block::copy");

  /*size_t len = static_cast<size_t> (this->end () - this->wr_ptr ());*/
  // Note that for this to work correct, end () *must* be >= mark ().
  size_t len = this->space ();

  if (len < n)
    {
      errno = ENOSPC;
      return -1;
    }
  else
    {
      (void) ACE_OS::memcpy (this->wr_ptr (),
                             buf,
                             n);
      this->wr_ptr (n);
      return 0;
    }
}

int
ACE_Message_Block::copy (const char *buf)
{
  ACE_TRACE ("ACE_Message_Block::copy");

  /* size_t len = static_cast<size_t> (this->end () - this->wr_ptr ()); */
  // Note that for this to work correct, end() *must* be >= wr_ptr().
  size_t len = this->space ();

  size_t buflen = ACE_OS::strlen (buf) + 1;

  if (len < buflen)
    {
      errno = ENOSPC;
      return -1;
    }
  else
    {
      (void) ACE_OS::memcpy (this->wr_ptr (),
                             buf,
                             buflen);
      this->wr_ptr (buflen);
      return 0;
    }
}

int
ACE_Message_Block::crunch ()
{
  if (this->rd_ptr_ != 0)
    {
      if (this->rd_ptr_ > this->wr_ptr_)
        return -1;

      size_t const len = this->length ();
      (void) ACE_OS::memmove (this->base (),
                              this->rd_ptr (),
                              len);
      this->rd_ptr (this->base ());
      this->wr_ptr (this->base () + len);
    }
  return 0;
}

void
ACE_Data_Block::dump () const
{
#if defined (ACE_HAS_DUMP)
  ACE_TRACE ("ACE_Data_Block::dump");
  ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
  ACELIB_DEBUG ((LM_DEBUG,
              ACE_TEXT ("-----( Data Block )-----\n")
              ACE_TEXT ("type_ = %d\n")
              ACE_TEXT ("cur_size_ = %u\n")
              ACE_TEXT ("max_size_ = %u\n")
              ACE_TEXT ("flags_ = %u\n")
              ACE_TEXT ("base_ = %@\n")
              ACE_TEXT ("locking_strategy_ = %u\n")
              ACE_TEXT ("reference_count_ = %u\n")
              ACE_TEXT ("---------------------------\n"),
              this->type_,
              this->cur_size_,
              this->max_size_,
              this->flags_,
              this->base_,
              this->locking_strategy_,
              this->reference_count_));
  this->allocator_strategy_->dump ();
  ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP));
#endif /* ACE_HAS_DUMP */
}

void
ACE_Message_Block::dump () const
{
#if defined (ACE_HAS_DUMP)
  ACE_TRACE ("ACE_Message_Block::dump");
  ACELIB_DEBUG ((LM_DEBUG, ACE_BEGIN_DUMP, this));
  ACELIB_DEBUG ((LM_DEBUG,
              ACE_TEXT ("-----( Message Block )-----\n")
              ACE_TEXT ("priority_ = %d\n")
              ACE_TEXT ("next_ = %@\n")
              ACE_TEXT ("prev_ = %@\n")
              ACE_TEXT ("cont_ = %@\n")
              ACE_TEXT ("rd_ptr_ = %@\n")
              ACE_TEXT ("wr_ptr_ = %@\n")
              ACE_TEXT ("---------------------------\n"),
              this->priority_,
              this->next_,
              this->prev_,
              this->cont_,
              this->rd_ptr_,
              this->wr_ptr_));
  this->data_block ()->dump ();
  ACELIB_DEBUG ((LM_DEBUG, ACE_END_DUMP));
#endif /* ACE_HAS_DUMP */
}

int
ACE_Data_Block::reference_count () const
{
  if (this->locking_strategy_)
    {
      // We need to acquire the lock before retrieving the count
      ACE_GUARD_RETURN (ACE_Lock, ace_mon, *this->locking_strategy_, 0);

      return this->reference_count_i ();
    }

  return this->reference_count_i ();
}

int
ACE_Data_Block::size (size_t length)
{
  ACE_TRACE ("ACE_Data_Block::size");

  if (length <= this->max_size_)
    this->cur_size_ = length;
  else
    {
      // We need to resize!
      char *buf = 0;
      ACE_ALLOCATOR_RETURN (buf,
                            (char *) this->allocator_strategy_->malloc (length),
                            -1);

      ACE_OS::memcpy (buf,
                      this->base_,
                      this->cur_size_);
      if (ACE_BIT_DISABLED (this->flags_,
                            ACE_Message_Block::DONT_DELETE))
        this->allocator_strategy_->free ((void *) this->base_);
      else
        // We now assume ownership.
        ACE_CLR_BITS (this->flags_,
                      ACE_Message_Block::DONT_DELETE);
      this->max_size_ = length;
      this->cur_size_ = length;
      this->base_ = buf;
    }
  return 0;
}

int
ACE_Message_Block::size (size_t length)
{
  ACE_TRACE ("ACE_Message_Block::size");

  // Resize the underlying <ACE_Data_Block>.
  if (this->data_block ()->size (length) == -1)
    return -1;

  return 0;
}

void
ACE_Message_Block::total_size_and_length (size_t &mb_size,
                                          size_t &mb_length) const
{
  ACE_TRACE ("ACE_Message_Block::total_size_and_length");

  for (const ACE_Message_Block *i = this;
       i != 0;
       i = i->cont ())
    {
      mb_size += i->size ();
      mb_length += i->length ();
    }
}

size_t
ACE_Message_Block::total_size () const
{
  ACE_TRACE ("ACE_Message_Block::total_size");

  size_t size = 0;
  for (const ACE_Message_Block *i = this;
       i != 0;
       i = i->cont ())
    size += i->size ();

  return size;
}

size_t
ACE_Message_Block::total_length () const
{
  ACE_TRACE ("ACE_Message_Block::total_length");

  size_t length = 0;
  for (const ACE_Message_Block *i = this;
       i != 0;
       i = i->cont ())
    length += i->length ();

  return length;
}

size_t
ACE_Message_Block::total_capacity () const
{
  ACE_TRACE ("ACE_Message_Block::total_capacity");

  size_t size = 0;

  for (const ACE_Message_Block *i = this;
       i != 0;
       i = i->cont ())
    size += i->capacity ();

  return size;
}

ACE_Data_Block::ACE_Data_Block ()
  : type_ (ACE_Message_Block::MB_DATA),
    cur_size_ (0),
    max_size_ (0),
    flags_ (ACE_Message_Block::DONT_DELETE),
    base_ (0),
    allocator_strategy_ (0),
    locking_strategy_ (0),
    reference_count_ (1),
    data_block_allocator_ (0)
{
  ACE_TRACE ("ACE_Data_Block::ACE_Data_Block");
  ACE_FUNCTION_TIMEPROBE (ACE_DATA_BLOCK_CTOR1_ENTER);

  ACE_ALLOCATOR (this->allocator_strategy_,
                 ACE_Allocator::instance ());

  ACE_ALLOCATOR (this->data_block_allocator_,
                 ACE_Allocator::instance ());
}

ACE_Data_Block::ACE_Data_Block (size_t size,
                                ACE_Message_Block::ACE_Message_Type msg_type,
                                const char *msg_data,
                                ACE_Allocator *allocator_strategy,
                                ACE_Lock *locking_strategy,
                                ACE_Message_Block::Message_Flags flags,
                                ACE_Allocator *data_block_allocator)
  : type_ (msg_type),
    cur_size_ (0),          // Reset later if memory alloc'd ok
    max_size_ (0),
    flags_ (flags),
    base_ (const_cast <char *> (msg_data)),
    allocator_strategy_ (allocator_strategy),
    locking_strategy_ (locking_strategy),
    reference_count_ (1),
    data_block_allocator_ (data_block_allocator)
{
  ACE_TRACE ("ACE_Data_Block::ACE_Data_Block");
  ACE_FUNCTION_TIMEPROBE (ACE_DATA_BLOCK_CTOR2_ENTER);

  // If the user didn't pass one in, let's use the
  // <ACE_Allocator::instance>.
  if (this->allocator_strategy_ == 0)
    ACE_ALLOCATOR (this->allocator_strategy_,
                   ACE_Allocator::instance ());

  if (this->data_block_allocator_ == 0)
    ACE_ALLOCATOR (this->data_block_allocator_,
                   ACE_Allocator::instance ());

  if (msg_data == 0)
    {
      ACE_ALLOCATOR (this->base_,
                     (char *) this->allocator_strategy_->malloc (size));
#if defined (ACE_INITIALIZE_MEMORY_BEFORE_USE)
      (void) ACE_OS::memset (this->base_,
                             '\0',
                             size);
#endif /* ACE_INITIALIZE_MEMORY_BEFORE_USE */
    }

  // ACE_ALLOCATOR returns on alloc failure but we cant throw, so setting
  // the size to 0 (i.e. "bad bit") ...
  if (this->base_ == 0)
    {
      size = 0;
    }

  // The memory is legit, whether passed in or allocated, so set
  // the size.
  this->cur_size_ = this->max_size_ = size;
}

ACE_Message_Block::ACE_Message_Block (const char *data,
                                      size_t size,
                                      unsigned long priority)
  : flags_ (0),
    data_block_ (0)
{
  ACE_TRACE ("ACE_Message_Block::ACE_Message_Block");

  if (this->init_i (size,    // size
                    MB_DATA, // type
                    nullptr, // cont
                    data,    // data
                    nullptr, // allocator
                    0,       // locking strategy
                    ACE_Message_Block::DONT_DELETE, // flags
                    priority, // priority
                    ACE_Time_Value::zero,     // execution time
                    ACE_Time_Value::max_time, // absolute time of deadline
                    nullptr,  // data block
                    nullptr,  // data_block allocator
                    0) == -1) // message_block allocator
    ACELIB_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Message_Block")));
}

ACE_Message_Block::ACE_Message_Block (ACE_Allocator *message_block_allocator)
  : flags_ (0),
    data_block_ (0)
{
  ACE_TRACE ("ACE_Message_Block::ACE_Message_Block");

  if (this->init_i (0,       // size
                    MB_DATA, // type
                    nullptr, // cont
                    nullptr, // data
                    nullptr, // allocator
                    0,       // locking strategy
                    ACE_Message_Block::DONT_DELETE, // flags
                    0, // priority
                    ACE_Time_Value::zero,     // execution time
                    ACE_Time_Value::max_time, // absolute time of deadline
                    nullptr, // data block
                    nullptr, // data_block allocator
                    message_block_allocator) == -1) // message_block allocator
    ACELIB_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Message_Block")));
}

ACE_Message_Block::ACE_Message_Block (size_t size,
                                      ACE_Message_Type msg_type,
                                      ACE_Message_Block *msg_cont,
                                      const char *msg_data,
                                      ACE_Allocator *allocator_strategy,
                                      ACE_Lock *locking_strategy,
                                      unsigned long priority,
                                      const ACE_Time_Value &execution_time,
                                      const ACE_Time_Value &deadline_time,
                                      ACE_Allocator *data_block_allocator,
                                      ACE_Allocator *message_block_allocator)
  :flags_ (0),
   data_block_ (0)
{
  ACE_TRACE ("ACE_Message_Block::ACE_Message_Block");

  if (this->init_i (size,
                    msg_type,
                    msg_cont,
                    msg_data,
                    allocator_strategy,
                    locking_strategy,
                    msg_data ? ACE_Message_Block::DONT_DELETE : 0,
                    priority,
                    execution_time,
                    deadline_time,
                    0, // data block
                    data_block_allocator,
                    message_block_allocator) == -1)
    ACELIB_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Message_Block")));
}

int
ACE_Message_Block::init (size_t size,
                         ACE_Message_Type msg_type,
                         ACE_Message_Block *msg_cont,
                         const char *msg_data,
                         ACE_Allocator *allocator_strategy,
                         ACE_Lock *locking_strategy,
                         unsigned long priority,
                         const ACE_Time_Value &execution_time,
                         const ACE_Time_Value &deadline_time,
                         ACE_Allocator *data_block_allocator,
                         ACE_Allocator *message_block_allocator)
{
  ACE_TRACE ("ACE_Message_Block::init");

  return this->init_i (size,
                       msg_type,
                       msg_cont,
                       msg_data,
                       allocator_strategy,
                       locking_strategy,
                       msg_data ? ACE_Message_Block::DONT_DELETE : 0,
                       priority,
                       execution_time,
                       deadline_time,
                       0,  // data block
                       data_block_allocator,
                       message_block_allocator);
}

int
ACE_Message_Block::init (const char *data, size_t size)
{
  ACE_TRACE ("ACE_Message_Block::init");
  // Should we also initialize all the other fields, as well?

  return this->init_i (size,    // size
                       MB_DATA, // type
                       nullptr, // cont
                       data,    // data
                       nullptr, // allocator
                       0,       // locking strategy
                       ACE_Message_Block::DONT_DELETE,  // flags
                       0,  // priority
                       ACE_Time_Value::zero,     // execution time
                       ACE_Time_Value::max_time, // absolute time of deadline
                       nullptr,  // data block
                       nullptr,  // data_block allocator
                       nullptr); // message_block allocator
}

ACE_Message_Block::ACE_Message_Block (size_t size,
                                      ACE_Message_Type msg_type,
                                      ACE_Message_Block *msg_cont,
                                      const char *msg_data,
                                      ACE_Allocator *allocator_strategy,
                                      ACE_Lock *locking_strategy,
                                      Message_Flags flags,
                                      unsigned long priority,
                                      const ACE_Time_Value &execution_time,
                                      const ACE_Time_Value &deadline_time,
                                      ACE_Data_Block *db,
                                      ACE_Allocator *data_block_allocator,
                                      ACE_Allocator *message_block_allocator)
  : flags_ (0),
    data_block_ (nullptr)
{
  ACE_TRACE ("ACE_Message_Block::ACE_Message_Block");

  if (this->init_i (size,
                    msg_type,
                    msg_cont,
                    msg_data,
                    allocator_strategy,
                    locking_strategy,
                    flags,
                    priority,
                    execution_time,
                    deadline_time,
                    db,
                    data_block_allocator,
                    message_block_allocator) == -1)
    ACELIB_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Message_Block")));
}

ACE_Message_Block::ACE_Message_Block (ACE_Data_Block *data_block,
                                      ACE_Message_Block::Message_Flags flags,
                                      ACE_Allocator *message_block_allocator)
  : flags_ (flags),
    data_block_ (nullptr)
{
  ACE_TRACE ("ACE_Message_Block::ACE_Message_Block");

  if (this->init_i (0,         // size
                    MB_NORMAL, // type
                    0,         // cont
                    0,         // data
                    0,         // allocator
                    0,         // locking strategy
                    0,         // flags
                    0,         // priority
                    ACE_Time_Value::zero,     // execution time
                    ACE_Time_Value::max_time, // absolute time of deadline
                    data_block, // data block
                    data_block->data_block_allocator (),
                    message_block_allocator) == -1)
    ACELIB_ERROR ((LM_ERROR,
                ACE_TEXT ("ACE_Message_Block")));
}

ACE_Message_Block::ACE_Message_Block (const ACE_Message_Block &mb,
                                      size_t align)
  :flags_ (0),
   data_block_ (nullptr)
{
  ACE_TRACE ("ACE_Message_Block::ACE_Message_Block");

  if (ACE_BIT_DISABLED (mb.flags_,
                        ACE_Message_Block::DONT_DELETE))
    {
      if (this->init_i (0,         // size
                        MB_NORMAL, // type
                        nullptr,   // cont
                        nullptr,   // data
                        nullptr,   // allocator
                        nullptr,   // locking strategy
                        0,         // flags
                        0,         // priority
                        ACE_Time_Value::zero,     // execution time
                        ACE_Time_Value::max_time, // absolute time of deadline
                        mb.data_block ()->duplicate (), // data block
                        mb.data_block ()->data_block_allocator (),
                        mb.message_block_allocator_) == -1)
        ACELIB_ERROR ((LM_ERROR,
                    ACE_TEXT ("ACE_Message_Block")));
#if !defined (ACE_LACKS_CDR_ALIGNMENT)
      // Align ourselves
      char *start = ACE_ptr_align_binary (this->base (),
                                          align);
#else
      char *start = this->base ();
#endif /* ACE_LACKS_CDR_ALIGNMENT */

      // Set our rd & wr pointers
      this->rd_ptr (start);
      this->wr_ptr (start);
    }
  else
    {
      if (this->init_i (0,         // size
                        MB_NORMAL, // type
                        nullptr,   // cont
                        nullptr,   // data
                        nullptr,   // allocator
                        nullptr,   // locking strategy
                        0,         // flags
                        0,         // priority
                        ACE_Time_Value::zero,     // execution time
                        ACE_Time_Value::max_time, // absolute time of deadline
                        mb.data_block ()->clone_nocopy (),// data block
                        mb.data_block ()->data_block_allocator (),
                        mb.message_block_allocator_) == -1)
        ACELIB_ERROR ((LM_ERROR,
                    ACE_TEXT ("ACE_Message_Block")));

#if !defined (ACE_LACKS_CDR_ALIGNMENT)
      // Align ourselves
      char *start = ACE_ptr_align_binary (this->base (),
                                          align);
#else
      char *start = this->base ();
#endif /* ACE_LACKS_CDR_ALIGNMENT */

      // Set our rd & wr pointers
      this->rd_ptr (start);
      this->wr_ptr (start);

#if !defined (ACE_LACKS_CDR_ALIGNMENT)
      // Get the alignment offset of the incoming ACE_Message_Block
      start = ACE_ptr_align_binary (mb.base (), align);
#else
      start = mb.base ();
#endif /* ACE_LACKS_CDR_ALIGNMENT */

      // Actual offset for the incoming message block assuming that it
      // is also aligned to the same "align" byte
      size_t const wr_offset = mb.wr_ptr_ - (start - mb.base ());

      // Copy wr_offset amount of data in to <this->data_block>
      (void) ACE_OS::memcpy (this->wr_ptr (),
                             start,
                             wr_offset);

      // Don't move the write pointer, just leave it to the application
      // to do what it wants
    }
#if defined (ACE_LACKS_CDR_ALIGNMENT)
  ACE_UNUSED_ARG (align);
#endif /* ACE_LACKS_CDR_ALIGNMENT */
}

int
ACE_Message_Block::init_i (size_t size,
                           ACE_Message_Type msg_type,
                           ACE_Message_Block *msg_cont,
                           const char *msg_data,
                           ACE_Allocator *allocator_strategy,
                           ACE_Lock *locking_strategy,
                           Message_Flags flags,
                           unsigned long priority,
                           const ACE_Time_Value &execution_time,
                           const ACE_Time_Value &deadline_time,
                           ACE_Data_Block *db,
                           ACE_Allocator *data_block_allocator,
                           ACE_Allocator *message_block_allocator)
{
  ACE_TRACE ("ACE_Message_Block::init_i");
  ACE_FUNCTION_TIMEPROBE (ACE_MESSAGE_BLOCK_INIT_I_ENTER);

  this->rd_ptr_ = 0;
  this->wr_ptr_ = 0;
  this->priority_ = priority;
#if defined (ACE_HAS_TIMED_MESSAGE_BLOCKS)
  this->execution_time_ = execution_time;
  this->deadline_time_ = deadline_time;
#else
  ACE_UNUSED_ARG (execution_time);
  ACE_UNUSED_ARG (deadline_time);
#endif /* ACE_HAS_TIMED_MESSAGE_BLOCKS */
  this->cont_ = msg_cont;
  this->next_ = nullptr;
  this->prev_ = nullptr;

  this->message_block_allocator_ = message_block_allocator;

  if (this->data_block_ != 0)
    {
      this->data_block_->release ();
      this->data_block_ = 0;
    }

  if (db == 0)
    {
      if (data_block_allocator == 0)
        ACE_ALLOCATOR_RETURN (data_block_allocator,
                              ACE_Allocator::instance (),
                              -1);

      ACE_TIMEPROBE (ACE_MESSAGE_BLOCK_INIT_I_DB_ALLOC);

      // Allocate the <ACE_Data_Block> portion, which is reference
      // counted.
      ACE_NEW_MALLOC_RETURN (db,
                             static_cast<ACE_Data_Block *> (
                               data_block_allocator->malloc (sizeof (ACE_Data_Block))),
                             ACE_Data_Block (size,
                                             msg_type,
                                             msg_data,
                                             allocator_strategy,
                                             locking_strategy,
                                             flags,
                                             data_block_allocator),
                             -1);
      ACE_TIMEPROBE (ACE_MESSAGE_BLOCK_INIT_I_DB_CTOR);

      // Message block initialization may fail, while the construction
      // succeeds.  Since ACE may throw no exceptions, we have to do a
      // separate check and clean up, like this:
      if (db != 0 && db->size () < size)
        {
          db->ACE_Data_Block::~ACE_Data_Block();  // placement destructor ...
          data_block_allocator->free (db); // free ...
          errno = ENOMEM;
          return -1;
        }
    }

  // Reset the data_block_ pointer.
  this->data_block (db);

  return 0;
}

ACE_Data_Block::~ACE_Data_Block ()
{
  // Sanity check...
  ACE_ASSERT (this->reference_count_ <= 1);

  // Just to be safe...
  this->reference_count_ = 0;

  if (ACE_BIT_DISABLED (this->flags_,
                        ACE_Message_Block::DONT_DELETE))
    {
      this->allocator_strategy_->free ((void *) this->base_);
      this->base_ = nullptr;
    }
}

ACE_Data_Block *
ACE_Data_Block::release_i ()
{
  ACE_TRACE ("ACE_Data_Block::release_i");

  ACE_ASSERT (this->reference_count_ > 0);

  ACE_Data_Block *result = nullptr;

  // decrement reference count
  --this->reference_count_;

  if (this->reference_count_ == 0)
    // this will cause deletion of this
    result = nullptr;
  else
    result = this;

  return result;
}

ACE_Data_Block *
ACE_Data_Block::release_no_delete (ACE_Lock *lock)
{
  ACE_TRACE ("ACE_Data_Block::release_no_delete");

  ACE_Data_Block *result = nullptr;
  ACE_Lock *lock_to_be_used = nullptr;

  // Check if we were passed in a lock
  if (lock != 0)
    {
      // Make sure that the lock passed in and our lock are the same
      if (lock == this->locking_strategy_)
        // In this case no locking is required.
        lock_to_be_used = 0;

      // The lock passed in does not match our lock
      else
        // Lock to be used is our lock
        lock_to_be_used = this->locking_strategy_;
    }
  // This is the case when no lock was passed in
  else
    {
      // Lock to be used is our lock
      lock_to_be_used = this->locking_strategy_;
    }

  // If there's a locking strategy then we need to acquire the lock
  // before decrementing the count.
  if (lock_to_be_used != 0)
    {
      ACE_GUARD_RETURN (ACE_Lock, ace_mon, *lock_to_be_used, 0);

      result = this->release_i ();
    }
  else
    {
      result = this->release_i ();
    }

  return result;
}

ACE_Data_Block *
ACE_Data_Block::release (ACE_Lock *lock)
{
  ACE_TRACE ("ACE_Data_Block::release");

  ACE_Allocator *allocator = this->data_block_allocator_;

  ACE_Data_Block *result = this->release_no_delete (lock);

  // We must delete this outside the scope of the locking_strategy_
  // since otherwise we'd be trying to "release" through a deleted
  // pointer!
  if (result == nullptr)
    ACE_DES_FREE_THIS (allocator->free,
                       ACE_Data_Block);
  return result;
}

ACE_Message_Block *
ACE_Message_Block::release ()
{
  ACE_TRACE ("ACE_Message_Block::release");

  // We want to hold the data block in a temporary variable because we
  // invoked "delete this;" at some point, so using this->data_block_
  // could be a bad idea.
  ACE_Data_Block *tmp = this->data_block ();

  // This flag is set to 1 when we have to destroy the data_block
  int destroy_dblock = 0;

  ACE_Lock *lock = 0;

  // Do we have a valid data block
  if (this->data_block ())
    {
      // Grab the lock that belongs to my data block
      lock = this->data_block ()->locking_strategy ();

      // if we have a lock
      if (lock != 0)
        {
          // One guard for all
          ACE_GUARD_RETURN (ACE_Lock, ace_mon, *lock, 0);

          // Call non-guarded release with @a lock
          destroy_dblock = this->release_i (lock);
        }
      // This is the case when we have a valid data block but no lock
      else
        // Call non-guarded release with no lock
        destroy_dblock = this->release_i (0);
    }
  else
    // This is the case when we don't even have a valid data block
    destroy_dblock = this->release_i (0);

  if (destroy_dblock != 0)
    {
      ACE_Allocator *allocator = tmp->data_block_allocator ();
      ACE_DES_FREE (tmp,
                    allocator->free,
                    ACE_Data_Block);
    }

  return 0;
}

int
ACE_Message_Block::release_i (ACE_Lock *lock)
{
  ACE_TRACE ("ACE_Message_Block::release_i");

  // Free up all the continuation messages.
  if (this->cont_)
    {
      ACE_Message_Block *mb = this->cont_;
      ACE_Message_Block *tmp = nullptr;

      do
        {
          tmp = mb;
          mb = mb->cont_;
          tmp->cont_ = 0;

          ACE_Data_Block *db = tmp->data_block ();
          if (tmp->release_i (lock) != 0)
            {
              ACE_Allocator *allocator = db->data_block_allocator ();
              ACE_DES_FREE (db,
                            allocator->free,
                            ACE_Data_Block);
            }
        }
      while (mb);

      this->cont_ = 0;
    }

  int result = 0;

  if (ACE_BIT_DISABLED (this->flags_,
                        ACE_Message_Block::DONT_DELETE) &&
      this->data_block ())
    {
      if (this->data_block ()->release_no_delete (lock) == 0)
        result = 1;
      this->data_block_ = nullptr;
    }

  // We will now commit suicide: this object *must* have come from the
  // allocator given.
  if (this->message_block_allocator_ == nullptr)
    delete this;
  else
    {
      ACE_Allocator *allocator = this->message_block_allocator_;
      ACE_DES_FREE_THIS (allocator->free,
                         ACE_Message_Block);
    }

  return result;
}

/* static */ ACE_Message_Block *
ACE_Message_Block::release (ACE_Message_Block *mb)
{
  ACE_TRACE ("ACE_Message_Block::release");

  if (mb != nullptr)
    return mb->release ();
  else
    return nullptr;
}

ACE_Message_Block::~ACE_Message_Block ()
{
  ACE_TRACE ("ACE_Message_Block::~ACE_Message_Block");

  if (ACE_BIT_DISABLED (this->flags_, ACE_Message_Block::DONT_DELETE) &&
      this->data_block ())
    {
      this->data_block ()->release ();
    }

  this->prev_ = nullptr;
  this->next_ = nullptr;
  this->cont_ = nullptr;
}

ACE_Data_Block *
ACE_Data_Block::duplicate ()
{
  ACE_TRACE ("ACE_Data_Block::duplicate");

  // Create a new <ACE_Message_Block>, but share the <base_> pointer
  // data (i.e., don't copy that).
  if (this->locking_strategy_)
    {
      // We need to acquire the lock before incrementing the count.
      ACE_GUARD_RETURN (ACE_Lock, ace_mon, *this->locking_strategy_, 0);
      ++this->reference_count_;
    }
  else
    ++this->reference_count_;

  return this;
}

#if defined (ACE_HAS_TIMED_MESSAGE_BLOCKS)
#define ACE_EXECUTION_TIME this->execution_time_
#define ACE_DEADLINE_TIME this->deadline_time_
#else
#define ACE_EXECUTION_TIME ACE_Time_Value::zero
#define ACE_DEADLINE_TIME ACE_Time_Value::max_time
#endif /* ACE_HAS_TIMED_MESSAGE_BLOCKS */

ACE_Message_Block *
ACE_Message_Block::duplicate () const
{
  ACE_TRACE ("ACE_Message_Block::duplicate");

  ACE_Message_Block *nb_top = nullptr;
  ACE_Message_Block *nb = nullptr;
  const ACE_Message_Block *current = this;

  // Increment the reference counts of all the continuation messages.
  while (current)
    {
      ACE_Message_Block* cur_dup = 0;

      // Create a new <ACE_Message_Block> that contains unique copies of
      // the message block fields, but a reference counted duplicate of
      // the <ACE_Data_Block>.

      // If there is no allocator, use the standard new and delete calls.
      if (current->message_block_allocator_ == 0)
        ACE_NEW_NORETURN (cur_dup,
                          ACE_Message_Block (0, // size
                                             ACE_Message_Type (0), // type
                                             nullptr, // cont
                                             nullptr, // data
                                             nullptr, // allocator
                                             nullptr, // locking strategy
                                             0, // flags
                                             current->priority_, // priority
                                             ACE_EXECUTION_TIME,
                                             ACE_DEADLINE_TIME,
                                             // Get a pointer to a
                                             // "duplicated" <ACE_Data_Block>
                                             // (will simply increment the
                                             // reference count).
                                             current->data_block ()->duplicate  (),
                                             current->data_block ()->data_block_allocator (),
                                             current->message_block_allocator_));
      else // Otherwise, use the message_block_allocator passed in.
        ACE_NEW_MALLOC_NORETURN (cur_dup,
                                 static_cast<ACE_Message_Block*> (
                                      current->message_block_allocator_->malloc (sizeof (ACE_Message_Block))),
                                 ACE_Message_Block (0, // size
                                                    ACE_Message_Type (0), // type
                                                    nullptr, // cont
                                                    nullptr, // data
                                                    nullptr, // allocator
                                                    nullptr, // locking strategy
                                                    0, // flags
                                                    current->priority_, // priority
                                                    ACE_EXECUTION_TIME,
                                                    ACE_DEADLINE_TIME,
                                                    // Get a pointer to a
                                                    // "duplicated" <ACE_Data_Block>
                                                    // (will simply increment the
                                                    // reference count).
                                                    current->data_block ()->duplicate  (),
                                                    current->data_block ()->data_block_allocator (),
                                                    current->message_block_allocator_));


      // If allocation failed above, release everything done so far and return NULL
      if (cur_dup == 0)
        {
          if (nb_top != 0)
            {
              nb_top->release ();
            }
          return 0;
        }

      // Set the read and write pointers in the new <Message_Block> to the
      // same relative offset as in the existing <Message_Block>.  Note
      // that we are assuming that the data_block()->base() pointer
      // doesn't change when it's duplicated.
      cur_dup->rd_ptr (current->rd_ptr_);
      cur_dup->wr_ptr (current->wr_ptr_);

      if (!nb)
        {
          /* First in the list: set leading pointers */
          nb_top = nb = cur_dup;
        }
      else
        {
          /* Continuing on: append to nb and walk down the list */
          nb->cont_ = cur_dup;
          nb = nb->cont_;
        }

      current = current->cont_;
    }

  return nb_top;
}

ACE_Message_Block *
ACE_Message_Block::duplicate (const ACE_Message_Block *mb)
{
  ACE_TRACE ("ACE_Message_Block::duplicate");
  if (mb == nullptr)
    return nullptr;
  else
    return mb->duplicate ();
}

ACE_Data_Block *
ACE_Data_Block::clone (ACE_Message_Block::Message_Flags mask) const
{
  ACE_TRACE ("ACE_Data_Block::clone");

  ACE_Data_Block *nb = this->clone_nocopy (mask);

  // Copy all of the payload memory into the new object. The new block
  // was allocated with max_size_ (and, thus, it's cur_size_ is the same
  // as max_size_). Maintain the same "has been written" boundary in the
  // new block by only copying cur_size_ bytes.
  if (nb != nullptr)
    {
      ACE_OS::memcpy (nb->base_,
                      this->base_,
                      this->cur_size_);
    }

  return nb;
}

ACE_Data_Block *
ACE_Data_Block::clone_nocopy (ACE_Message_Block::Message_Flags mask,
                              size_t max_size) const
{
  ACE_FUNCTION_TIMEPROBE(ACE_DATA_BLOCK_CLONE_ENTER);

  ACE_TRACE ("ACE_Data_Block::clone_nocopy");

  // You always want to clear this one to prevent memory leaks but you
  // might add some others later.
  const ACE_Message_Block::Message_Flags always_clear = ACE_Message_Block::DONT_DELETE;
  const size_t newsize = max_size == 0 ? this->max_size_ : max_size;
  ACE_Data_Block *nb = nullptr;

  ACE_NEW_MALLOC_RETURN (nb,
                         static_cast<ACE_Data_Block*> (
                           this->data_block_allocator_->malloc (sizeof (ACE_Data_Block))),
                         ACE_Data_Block (newsize, // size
                                         this->type_,     // type
                                         0,               // data
                                         this->allocator_strategy_, // allocator
                                         this->locking_strategy_, // locking strategy
                                         this->flags_,  // flags
                                         this->data_block_allocator_),
                         0);

  // Message block initialization may fail while the construction
  // succeeds.  Since as a matter of policy, ACE may throw no
  // exceptions, we have to do a separate check like this.
  if (nb != 0 && nb->size () < newsize)
    {
      nb->ACE_Data_Block::~ACE_Data_Block();  // placement destructor ...
      this->data_block_allocator_->free (nb); // free ...
      errno = ENOMEM;
      return 0;
    }

  // Set new flags minus the mask...
  nb->clr_flags (mask | always_clear);
  return nb;
}

ACE_Message_Block *
ACE_Message_Block::clone (Message_Flags mask) const
{
  ACE_TRACE ("ACE_Message_Block::clone");

  const ACE_Message_Block *old_message_block = this;
  ACE_Message_Block *new_message_block {};
  ACE_Message_Block *new_previous_message_block {};
  ACE_Message_Block *new_root_message_block {};

  do
    {
      // Get a pointer to a "cloned" ACE_Data_Block (will copy the
      // values rather than increment the reference count).
      ACE_Data_Block *db = old_message_block->data_block ()->clone (mask);

      if (!db)
        return nullptr;

      if(old_message_block->message_block_allocator_ == nullptr)
        {
          ACE_NEW_NORETURN (new_message_block,
                            ACE_Message_Block (0, // size
                                               ACE_Message_Type (0), // type
                                               nullptr, // cont
                                               nullptr, // data
                                               nullptr, // allocator
                                               nullptr, // locking strategy
                                               0, // flags
                                               old_message_block->priority_, // priority
                                               ACE_EXECUTION_TIME, // execution time
                                               ACE_DEADLINE_TIME, // absolute time to deadline
                                               // Get a pointer to a
                                               // "duplicated"<ACE_Data_Block>
                                               // (will simply increment the
                                               // reference count).
                                               db,
                                               db->data_block_allocator (),
                                               old_message_block->message_block_allocator_));
        }
      else
        {
          // This is the ACE_NEW_MALLOC macro with the return check removed.
          // We need to do it this way because if it fails we need to release
          // the cloned data block that was created above. If we used
          // ACE_NEW_MALLOC_RETURN, there would be a memory leak because the
          // above db pointer would be left dangling.
          new_message_block = static_cast<ACE_Message_Block*> (old_message_block->message_block_allocator_->malloc (sizeof (ACE_Message_Block)));
          if (new_message_block != nullptr)
            new (new_message_block) ACE_Message_Block (0, // size
                                                       ACE_Message_Type (0), // type
                                                       nullptr, // cont
                                                       nullptr, // data
                                                       nullptr, // allocator
                                                       nullptr, // locking strategy
                                                       0, // flags
                                                       old_message_block->priority_, // priority
                                                       ACE_EXECUTION_TIME, // execution time
                                                       ACE_DEADLINE_TIME, // absolute time to deadline
                                                       db,
                                                       db->data_block_allocator (),
                                                       old_message_block->message_block_allocator_);
        }

      if (new_message_block == nullptr)
        {
          db->release ();
          return nullptr;
        }

      // Set the read and write pointers in the new <Message_Block> to the
      // same relative offset as in the existing <Message_Block>.
      new_message_block->rd_ptr (old_message_block->rd_ptr_);
      new_message_block->wr_ptr (old_message_block->wr_ptr_);
      // save the root message block to return
      if (new_root_message_block == nullptr)
        new_root_message_block = new_message_block;
      if (new_previous_message_block != nullptr)
        // we're a continuation of the previous block, add ourself to its chain
        new_previous_message_block->cont_ = new_message_block;
      new_previous_message_block = new_message_block;
      old_message_block = old_message_block->cont ();
    }
  while (old_message_block != nullptr);

  return new_root_message_block;
}

// This is private.
ACE_Message_Block &
ACE_Message_Block::operator= (const ACE_Message_Block &)
{
  ACE_TRACE ("ACE_Message_Block::operator=");
  return *this;
}

void
ACE_Data_Block::base (char *msg_data,
                      size_t msg_length,
                      ACE_Message_Block::Message_Flags msg_flags)
{
  if (ACE_BIT_DISABLED (this->flags_,
                        ACE_Message_Block::DONT_DELETE))
    this->allocator_strategy_->free (this->base_);

  this->max_size_ = msg_length;
  this->cur_size_ = msg_length;
  this->base_ = msg_data;
  this->flags_ = msg_flags;
}

ACE_END_VERSIONED_NAMESPACE_DECL
