// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/fml/message.h"

#include "flutter/fml/logging.h"

namespace fml {

size_t MessageSerializable::GetSerializableTag() const {
  return 0;
};

Message::Message() = default;

Message::~Message() = default;

static uint32_t NextPowerOfTwoSize(uint32_t x) {
  if (x == 0) {
    return 1;
  }

  --x;

  x |= x >> 1;
  x |= x >> 2;
  x |= x >> 4;
  x |= x >> 8;
  x |= x >> 16;

  return x + 1;
}

const uint8_t* Message::GetBuffer() const {
  return buffer_;
}

size_t Message::GetBufferSize() const {
  return buffer_length_;
}

size_t Message::GetDataLength() const {
  return data_length_;
}

size_t Message::GetSizeRead() const {
  return size_read_;
}

bool Message::Reserve(size_t size) {
  if (buffer_length_ >= size) {
    return true;
  }
  return Resize(NextPowerOfTwoSize(size));
}

bool Message::Resize(size_t size) {
  if (buffer_ == nullptr) {
    // This is the initial resize where we have no previous buffer.
    FML_DCHECK(buffer_length_ == 0);

    void* buffer = ::malloc(size);
    const bool success = buffer != nullptr;

    if (success) {
      buffer_ = static_cast<uint8_t*>(buffer);
      buffer_length_ = size;
    }

    return success;
  }

  FML_DCHECK(size > buffer_length_);

  void* resized = ::realloc(buffer_, size);

  const bool success = resized != nullptr;

  // In case of failure, the input buffer to realloc is still valid.
  if (success) {
    buffer_ = static_cast<uint8_t*>(resized);
    buffer_length_ = size;
  }

  return success;
}

uint8_t* Message::PrepareEncode(size_t size) {
  if (!Reserve(data_length_ + size)) {
    return nullptr;
  }

  auto old_length = data_length_;
  data_length_ += size;
  return buffer_ + old_length;
}

uint8_t* Message::PrepareDecode(size_t size) {
  if ((size + size_read_) > buffer_length_) {
    return nullptr;
  }
  auto* buffer = buffer_ + size_read_;
  size_read_ += size;
  return buffer;
}

void Message::ResetRead() {
  size_read_ = 0;
}

}  // namespace fml