text: // Copyright 2018 The Fuchsia 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 "src/media/playback/mediaplayer/graph/payloads/payload_buffer.h"
  
  #include <lib/zx/vmar.h>
  
  #include <cstdlib>
  #include <memory>
  
  #include "src/lib/fxl/logging.h"
  
  namespace media_player {
  
  // static
  fbl::RefPtr<PayloadVmo> PayloadVmo::Create(uint64_t vmo_size, const zx::handle* bti_handle) {
    FXL_DCHECK(vmo_size != 0);
  
    zx::vmo vmo;
  
    if (bti_handle != nullptr) {
      // Create a contiguous VMO. This is a hack that will be removed once the
      // FIDL buffer allocator is working an integrated.
      zx_status_t status =
          zx_vmo_create_contiguous(bti_handle->get(), vmo_size, 0, vmo.reset_and_get_address());
      if (status != ZX_OK) {
        FXL_LOG(ERROR) << "Failed to create contiguous VMO of size " << vmo_size << ", status "
                       << status << ".";
        return nullptr;
      }
    } else {
      zx_status_t status = zx::vmo::create(vmo_size, 0, &vmo);
      if (status != ZX_OK) {
        FXL_LOG(ERROR) << "Failed to create VMO of size " << vmo_size << ", status " << status << ".";
        return nullptr;
      }
    }
  
    zx_status_t status;
    auto result = fbl::MakeRefCounted<PayloadVmo>(
        std::move(vmo), vmo_size, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, bti_handle, &status);
    return status == ZX_OK ? result : nullptr;
  }
  
  // static
  fbl::RefPtr<PayloadVmo> PayloadVmo::Create(zx::vmo vmo, zx_vm_option_t map_flags) {
    uint64_t vmo_size;
    zx_status_t status = vmo.get_size(&vmo_size);
    if (status != ZX_OK) {
      FXL_LOG(ERROR) << "Failed to get VMO size.";
      return nullptr;
    }
  
    auto result =
        fbl::MakeRefCounted<PayloadVmo>(std::move(vmo), vmo_size, map_flags, nullptr, &status);
    return status == ZX_OK ? result : nullptr;
  }
  
  PayloadVmo::PayloadVmo(zx::vmo vmo, uint64_t vmo_size, zx_vm_option_t map_flags,
                         const zx::handle* bti_handle, zx_status_t* status_out)
      : vmo_(std::move(vmo)), size_(vmo_size) {
    FXL_DCHECK(vmo_);
    FXL_DCHECK(vmo_size != 0);
    FXL_DCHECK(status_out != nullptr);
  
    zx_status_t status = vmo_mapper_.Map(vmo_, 0, size_, map_flags, nullptr);
    if (status != ZX_OK) {
      FXL_LOG(ERROR) << "Failed to map VMO, status " << status;
      *status_out = status;
      return;
    }
  
    *status_out = ZX_OK;
  }
  
  zx::vmo PayloadVmo::Duplicate(zx_rights_t rights) {
    zx::vmo duplicate;
    zx_status_t status = vmo_.duplicate(rights, &duplicate);
    if (status != ZX_OK) {
      FXL_LOG(FATAL) << "Failed to duplicate VMO, status " << status;
    }
  
    return duplicate;
  }
  
  // static
  fbl::RefPtr<PayloadBuffer> PayloadBuffer::Create(uint64_t size, void* data, Recycler recycler) {
    return fbl::AdoptRef(new PayloadBuffer(size, data, std::move(recycler)));
  }
  
  // static
  fbl::RefPtr<PayloadBuffer> PayloadBuffer::Create(uint64_t size, void* data,
                                                   fbl::RefPtr<PayloadVmo> vmo, uint64_t offset,
                                                   Recycler recycler) {
    return fbl::AdoptRef(new PayloadBuffer(size, data, vmo, offset, std::move(recycler)));
  }
  
  // static
  fbl::RefPtr<PayloadBuffer> PayloadBuffer::CreateWithMalloc(uint64_t size) {
    FXL_DCHECK(size > 0);
    // TODO: Once we use C++17, std::aligned_alloc should work.
    // |aligned_alloc| requires the size to the aligned.
    return PayloadBuffer::Create(
        size, aligned_alloc(PayloadBuffer::kByteAlignment, PayloadBuffer::AlignUp(size)),
        [](PayloadBuffer* payload_buffer) {
          FXL_DCHECK(payload_buffer);
          std::free(payload_buffer->data());
          // The |PayloadBuffer| deletes itself.
        });
  }
  
  PayloadBuffer::PayloadBuffer(uint64_t size, void* data, Recycler recycler)
      : size_(size), data_(data), recycler_(std::move(recycler)) {
    FXL_DCHECK(size_ != 0);
    FXL_DCHECK(data_ != nullptr);
    FXL_DCHECK(recycler_);
  }
  
  PayloadBuffer::PayloadBuffer(uint64_t size, void* data, fbl::RefPtr<PayloadVmo> vmo,
                               uint64_t offset_in_vmo, Recycler recycler)
      : size_(size), data_(data), vmo_(vmo), offset_(offset_in_vmo), recycler_(std::move(recycler)) {
    FXL_DCHECK(size_ != 0);
    FXL_DCHECK(vmo_);
    FXL_DCHECK((data_ == nullptr) || (reinterpret_cast<uint8_t*>(vmo_->start()) + offset_ ==
                                      reinterpret_cast<uint8_t*>(data_)));
    FXL_DCHECK(recycler_);
  
    // TODO(dalesat): Remove this check when we support unmappable VMOs.
    FXL_DCHECK(data_ != nullptr);
  }
  
  PayloadBuffer::~PayloadBuffer() {
    FXL_DCHECK(!recycler_) << "PayloadBuffers must delete themselves.";
  }
  
  void PayloadBuffer::AfterRecycling(Action action) {
    FXL_DCHECK(!after_recycling_) << "AfterRecycling may only be called once.";
    after_recycling_ = std::move(action);
  }
  
  void PayloadBuffer::fbl_recycle() {
    FXL_DCHECK(recycler_ != nullptr);
  
    recycler_(this);
    // This tells the destructor that deletion is being done properly.
    recycler_ = nullptr;
  
    if (after_recycling_) {
      after_recycling_(this);
    }
  
    delete this;
  }
  
  }  // namespace media_player
  