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 <lib/fdio/fd.h>
  #include <lib/zxio/null.h>
  #include <lib/zxio/ops.h>
  #include <zircon/syscalls.h>
  
  #include "internal.h"
  #include "zxio.h"
  
  struct fdio_waitable_t {
    zxio_t io;
  
    // arbitrary handle
    std::variant<zx::handle, zx::unowned_handle> handle;
  
    // signals that cause ZXIO_SIGNAL_READABLE
    zx_signals_t readable;
  
    // signals that cause ZXIO_SIGNAL_WRITABLE
    zx_signals_t writable;
  };
  
  static_assert(sizeof(fdio_waitable_t) <= sizeof(zxio_storage_t),
                "fdio_waitable_t must fit inside zxio_storage_t.");
  
  static zx_status_t fdio_waitable_close(zxio_t* io) {
    auto waitable = reinterpret_cast<fdio_waitable_t*>(io);
    waitable->~fdio_waitable_t();
    return ZX_OK;
  }
  
  static void fdio_waitable_wait_begin(zxio_t* io, zxio_signals_t zxio_signals,
                                       zx_handle_t* out_handle, zx_signals_t* out_zx_signals) {
    fdio_waitable_t* waitable = reinterpret_cast<fdio_waitable_t*>(io);
    zx_signals_t zx_signals = ZX_SIGNAL_NONE;
    if (zxio_signals & ZXIO_SIGNAL_READABLE) {
      zx_signals |= waitable->readable;
    }
    if (zxio_signals & ZXIO_SIGNAL_WRITABLE) {
      zx_signals |= waitable->writable;
    }
    std::visit(fdio::overloaded{
                   [out_handle](zx::handle& handle) { *out_handle = handle.get(); },
                   [out_handle](zx::unowned_handle& handle) { *out_handle = handle->get(); },
               },
               waitable->handle);
    *out_zx_signals = zx_signals;
  }
  
  static void fdio_waitable_wait_end(zxio_t* io, zx_signals_t zx_signals,
                                     zxio_signals_t* out_zxio_signals) {
    fdio_waitable_t* waitable = reinterpret_cast<fdio_waitable_t*>(io);
    zxio_signals_t zxio_signals = ZXIO_SIGNAL_NONE;
    if (zx_signals & waitable->readable) {
      zxio_signals |= ZXIO_SIGNAL_READABLE;
    }
    if (zx_signals & waitable->writable) {
      zxio_signals |= ZXIO_SIGNAL_WRITABLE;
    }
    *out_zxio_signals = zxio_signals;
  }
  
  static constexpr zxio_ops_t fdio_waitable_ops = []() {
    zxio_ops_t ops = zxio_default_ops;
    ops.close = fdio_waitable_close;
    ops.wait_begin = fdio_waitable_wait_begin;
    ops.wait_end = fdio_waitable_wait_end;
    return ops;
  }();
  
  zx::status<fdio_ptr> fdio_waitable_create(std::variant<zx::handle, zx::unowned_handle> handle,
                                            zx_signals_t readable, zx_signals_t writable) {
    zx::status io = fdio_internal::zxio::create();
    if (io.is_error()) {
      return io.take_error();
    }
    auto waitable = new (&io->zxio_storage()) fdio_waitable_t{
        .handle = std::move(handle),
        .readable = readable,
        .writable = writable,
    };
    zxio_init(&waitable->io, &fdio_waitable_ops);
    return io;
  }
  