Add Fuchsia SDK.
Bug: 120856007
Test: Build walleye.
Change-Id: I6b5ebe9bff4e729783bca67d35c291878fbe16cb
diff --git a/pkg/async-default/include/lib/async/default.h b/pkg/async-default/include/lib/async/default.h
new file mode 100644
index 0000000..b171058
--- /dev/null
+++ b/pkg/async-default/include/lib/async/default.h
@@ -0,0 +1,23 @@
+// Copyright 2017 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.
+
+#ifndef LIB_ASYNC_DEFAULT_H_
+#define LIB_ASYNC_DEFAULT_H_
+
+#include <lib/async/dispatcher.h>
+#include <zircon/compiler.h>
+
+__BEGIN_CDECLS
+
+// Gets the current thread's default asynchronous dispatcher interface.
+// Returns |NULL| if none.
+__EXPORT async_dispatcher_t* async_get_default_dispatcher(void);
+
+// Sets the current thread's default asynchronous dispatcher interface.
+// May be set to |NULL| if this thread doesn't have a default dispatcher.
+__EXPORT void async_set_default_dispatcher(async_dispatcher_t* dispatcher);
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_DEFAULT_H_
diff --git a/pkg/async-default/meta.json b/pkg/async-default/meta.json
new file mode 100644
index 0000000..2394825
--- /dev/null
+++ b/pkg/async-default/meta.json
@@ -0,0 +1,23 @@
+{
+ "binaries": {
+ "arm64": {
+ "debug": ".build-id/48/624ebaefd10ab212943f16760a00f469ec307e.debug",
+ "dist": "arch/arm64/dist/libasync-default.so",
+ "link": "arch/arm64/lib/libasync-default.so"
+ },
+ "x64": {
+ "debug": ".build-id/5e/3c55aea09e8943ae76d3603cc8c0b1ba27bfb7.debug",
+ "dist": "arch/x64/dist/libasync-default.so",
+ "link": "arch/x64/lib/libasync-default.so"
+ }
+ },
+ "deps": [],
+ "format": "shared",
+ "headers": [
+ "pkg/async-default/include/lib/async/default.h"
+ ],
+ "include_dir": "pkg/async-default/include",
+ "name": "async-default",
+ "root": "pkg/async-default",
+ "type": "cc_prebuilt_library"
+}
\ No newline at end of file
diff --git a/pkg/async-loop-cpp/include/lib/async-loop/cpp/loop.h b/pkg/async-loop-cpp/include/lib/async-loop/cpp/loop.h
new file mode 100644
index 0000000..6168aba
--- /dev/null
+++ b/pkg/async-loop-cpp/include/lib/async-loop/cpp/loop.h
@@ -0,0 +1,121 @@
+// Copyright 2017 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.
+
+#pragma once
+
+#include <lib/async-loop/loop.h>
+#include <lib/async/default.h>
+#include <lib/zx/time.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <threads.h>
+#include <zircon/compiler.h>
+
+namespace async {
+
+// C++ wrapper for an asynchronous dispatch loop.
+//
+// This class is thread-safe.
+class Loop {
+public:
+ // Creates a message loop.
+ // All operations on the message loop are thread-safe (except destroy).
+ //
+ // Note that it's ok to run the loop on a different thread from the one
+ // upon which it was created.
+ //
+ // |config| provides configuration for the message loop. Must not be null.
+ //
+ // See also |kAsyncLoopConfigAttachToThread| and |kAsyncLoopConfigNoAttachToThread|.
+ explicit Loop(const async_loop_config_t* config);
+
+ Loop(const Loop&) = delete;
+ Loop(Loop&&) = delete;
+ Loop& operator=(const Loop&) = delete;
+ Loop& operator=(Loop&&) = delete;
+
+ // Destroys the message loop.
+ // Implicitly calls |Shutdown()|.
+ ~Loop();
+
+ // Gets the underlying message loop structure.
+ async_loop_t* loop() const { return loop_; }
+
+ // Gets the loop's asynchronous dispatch interface.
+ async_dispatcher_t* dispatcher() const { return async_loop_get_dispatcher(loop_); }
+
+ // Shuts down the message loop, notifies handlers which asked to handle shutdown.
+ // The message loop must not currently be running on any threads other than
+ // those started by |StartThread()| which this function will join.
+ //
+ // Does nothing if already shutting down.
+ void Shutdown();
+
+ // Runs the message loop on the current thread.
+ // This function can be called on multiple threads to setup a multi-threaded
+ // dispatcher.
+ //
+ // Dispatches events until the |deadline| expires or the loop is quitted.
+ // Use |ZX_TIME_INFINITE| to dispatch events indefinitely.
+ //
+ // If |once| is true, performs a single unit of work then returns.
+ //
+ // Returns |ZX_OK| if the dispatcher returns after one cycle.
+ // Returns |ZX_ERR_TIMED_OUT| if the deadline expired.
+ // Returns |ZX_ERR_CANCELED| if the loop quitted.
+ // Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |Shutdown()|.
+ zx_status_t Run(zx::time deadline = zx::time::infinite(), bool once = false);
+
+ // Dispatches events until there are none remaining, and then returns
+ // without waiting. This is useful for unit testing, because the behavior
+ // doesn't depend on time.
+ //
+ // Returns |ZX_OK| if the dispatcher reaches an idle state.
+ // Returns |ZX_ERR_CANCELED| if the loop quitted.
+ // Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |Shutdown()|.
+ zx_status_t RunUntilIdle();
+
+ // Quits the message loop.
+ // Active invocations of |Run()| and threads started using |StartThread()|
+ // will eventually terminate upon completion of their current unit of work.
+ //
+ // Subsequent calls to |Run()| or |StartThread()| will return immediately
+ // until |ResetQuit()| is called.
+ void Quit();
+
+ // Resets the quit state of the message loop so that it can be restarted
+ // using |Run()| or |StartThread()|.
+ //
+ // This function must only be called when the message loop is not running.
+ // The caller must ensure all active invocations of |Run()| and threads
+ // started using |StartThread()| have terminated before resetting the quit state.
+ //
+ // Returns |ZX_OK| if the loop's state was |ASYNC_LOOP_RUNNABLE| or |ASYNC_LOOP_QUIT|.
+ // Returns |ZX_ERR_BAD_STATE| if the loop's state was |ASYNC_LOOP_SHUTDOWN| or if
+ // the message loop is currently active on one or more threads.
+ zx_status_t ResetQuit();
+
+ // Returns the current state of the message loop.
+ async_loop_state_t GetState() const;
+
+ // Starts a message loop running on a new thread.
+ // The thread will run until the loop quits.
+ //
+ // |name| is the desired name for the new thread, may be NULL.
+ // If |out_thread| is not NULL, it is set to the new thread identifier.
+ //
+ // Returns |ZX_OK| on success.
+ // Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |async_loop_shutdown()|.
+ // Returns |ZX_ERR_NO_MEMORY| if allocation or thread creation failed.
+ zx_status_t StartThread(const char* name = nullptr, thrd_t* out_thread = nullptr);
+
+ // Blocks until all dispatch threads started with |StartThread()|
+ // have terminated.
+ void JoinThreads();
+
+private:
+ async_loop_t* loop_;
+};
+
+} // namespace async
diff --git a/pkg/async-loop-cpp/loop_wrapper.cpp b/pkg/async-loop-cpp/loop_wrapper.cpp
new file mode 100644
index 0000000..cc866ec
--- /dev/null
+++ b/pkg/async-loop-cpp/loop_wrapper.cpp
@@ -0,0 +1,52 @@
+// Copyright 2017 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/async-loop/cpp/loop.h>
+
+#include <zircon/assert.h>
+
+namespace async {
+
+Loop::Loop(const async_loop_config_t* config) {
+ zx_status_t status = async_loop_create(config, &loop_);
+ ZX_ASSERT_MSG(status == ZX_OK, "status=%d", status);
+}
+
+Loop::~Loop() {
+ async_loop_destroy(loop_);
+}
+
+void Loop::Shutdown() {
+ async_loop_shutdown(loop_);
+}
+
+zx_status_t Loop::Run(zx::time deadline, bool once) {
+ return async_loop_run(loop_, deadline.get(), once);
+}
+
+zx_status_t Loop::RunUntilIdle() {
+ return async_loop_run_until_idle(loop_);
+}
+
+void Loop::Quit() {
+ async_loop_quit(loop_);
+}
+
+zx_status_t Loop::ResetQuit() {
+ return async_loop_reset_quit(loop_);
+}
+
+async_loop_state_t Loop::GetState() const {
+ return async_loop_get_state(loop_);
+}
+
+zx_status_t Loop::StartThread(const char* name, thrd_t* out_thread) {
+ return async_loop_start_thread(loop_, name, out_thread);
+}
+
+void Loop::JoinThreads() {
+ async_loop_join_threads(loop_);
+}
+
+} // namespace async
diff --git a/pkg/async-loop-cpp/meta.json b/pkg/async-loop-cpp/meta.json
new file mode 100644
index 0000000..950712f
--- /dev/null
+++ b/pkg/async-loop-cpp/meta.json
@@ -0,0 +1,24 @@
+{
+ "banjo_deps": [],
+ "deps": [
+ "async",
+ "async-default",
+ "async-loop",
+ "zx"
+ ],
+ "fidl_deps": [],
+ "files": [
+ "pkg/async-loop-cpp/loop_wrapper.cpp",
+ "pkg/async-loop-cpp/include/lib/async-loop/cpp/loop.h"
+ ],
+ "headers": [
+ "pkg/async-loop-cpp/include/lib/async-loop/cpp/loop.h"
+ ],
+ "include_dir": "pkg/async-loop-cpp/include",
+ "name": "async-loop-cpp",
+ "root": "pkg/async-loop-cpp",
+ "sources": [
+ "pkg/async-loop-cpp/loop_wrapper.cpp"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/async-loop/include/lib/async-loop/loop.h b/pkg/async-loop/include/lib/async-loop/loop.h
new file mode 100644
index 0000000..1335cdb
--- /dev/null
+++ b/pkg/async-loop/include/lib/async-loop/loop.h
@@ -0,0 +1,177 @@
+// Copyright 2017 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.
+
+//
+// Provides an implementation of a simple thread-safe asynchronous
+// dispatcher based on a Zircon completion port. The implementation
+// is designed to avoid most dynamic memory allocation except for that
+// which is required to create the loop in the first place or to manage
+// the list of running threads.
+//
+// See README.md for example usage.
+//
+
+#ifndef LIB_ASYNC_LOOP_LOOP_H_
+#define LIB_ASYNC_LOOP_LOOP_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <threads.h>
+
+#include <zircon/compiler.h>
+
+#include <lib/async/dispatcher.h>
+
+__BEGIN_CDECLS
+
+// Pointer to a message loop created using |async_loop_create()|.
+typedef struct async_loop async_loop_t;
+
+// Message loop configuration structure.
+typedef void(async_loop_callback_t)(async_loop_t* loop, void* data);
+typedef struct async_loop_config {
+ // If true, the loop will automatically register itself as the default
+ // dispatcher for the thread upon which it was created and will
+ // automatically unregister itself when destroyed (which must occur on
+ // the same thread).
+ //
+ // If false, the loop will not do this. The loop's creator is then
+ // resposible for retrieving the loop's dispatcher using |async_loop_get_dispatcher()|
+ // and passing it around explicitly or calling |async_set_default_dispatcher()| as needed.
+ //
+ // Note that the loop can be used even without setting it as the current
+ // thread's default.
+ bool make_default_for_current_thread;
+
+ // A function to call before the dispatcher invokes each handler, or NULL if none.
+ async_loop_callback_t* prologue;
+
+ // A function to call after the dispatcher invokes each handler, or NULL if none.
+ async_loop_callback_t* epilogue;
+
+ // Data to pass to the callback functions.
+ void* data;
+} async_loop_config_t;
+
+// Simple config that when passed to async_loop_create will create a loop
+// that will automatically register itself as the default
+// dispatcher for the thread upon which it was created and will
+// automatically unregister itself when destroyed (which must occur on
+// the same thread).
+extern const async_loop_config_t kAsyncLoopConfigAttachToThread;
+
+// Simple config that when passed to async_loop_create will create a loop
+// that is not registered to the current thread.
+extern const async_loop_config_t kAsyncLoopConfigNoAttachToThread;
+
+// Creates a message loop and returns a pointer to it in |out_loop|.
+// All operations on the message loop are thread-safe (except destroy).
+//
+// Note that it's ok to run the loop on a different thread from the one
+// upon which it was created.
+//
+// |config| provides configuration for the message loop. Must not be null.
+//
+// Returns |ZX_OK| on success.
+// Returns |ZX_ERR_NO_MEMORY| if allocation failed.
+// May return other errors if the necessary internal handles could not be created.
+//
+// See also |kAsyncLoopConfigAttachToThread| and |kAsyncLoopConfigNoAttachToThread|.
+zx_status_t async_loop_create(const async_loop_config_t* config,
+ async_loop_t** out_loop);
+
+// Gets the the message loop's asynchronous dispatch interface.
+async_dispatcher_t* async_loop_get_dispatcher(async_loop_t* loop);
+
+// Gets the message loop associated with the specified asynchronous dispatch interface
+//
+// This function assumes the dispatcher is backed by an |async_loop_t| which was created
+// using |async_loop_create()|. Its behavior is undefined if used with other dispatcher
+// implementations.
+async_loop_t* async_loop_from_dispatcher(async_dispatcher_t* dispatcher);
+
+// Shuts down the message loop, notifies handlers which asked to handle shutdown.
+// The message loop must not currently be running on any threads other than
+// those started by |async_loop_start_thread()| which this function will join.
+//
+// Does nothing if already shutting down.
+void async_loop_shutdown(async_loop_t* loop);
+
+// Destroys the message loop.
+// Implicitly calls |async_loop_shutdown()| and joins all threads started
+// using |async_loop_start_thread()| before destroying the loop itself.
+void async_loop_destroy(async_loop_t* loop);
+
+// Runs the message loop on the current thread.
+// This function can be called on multiple threads to setup a multi-threaded
+// dispatcher.
+//
+// Dispatches events until the |deadline| expires or the loop is quitted.
+// Use |ZX_TIME_INFINITE| to dispatch events indefinitely.
+//
+// If |once| is true, performs a single unit of work then returns.
+//
+// Returns |ZX_OK| if the dispatcher returns after one cycle.
+// Returns |ZX_ERR_TIMED_OUT| if the deadline expired.
+// Returns |ZX_ERR_CANCELED| if the loop quitted.
+// Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |async_loop_shutdown()|.
+zx_status_t async_loop_run(async_loop_t* loop, zx_time_t deadline, bool once);
+
+// Dispatches events until there are none remaining, and then returns without
+// waiting. This is useful for unit testing, because the behavior doesn't depend
+// on time.
+//
+// Returns |ZX_OK| if the dispatcher reaches an idle state.
+// Returns |ZX_ERR_CANCELED| if the loop quitted.
+// Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |async_loop_shutdown()|.
+zx_status_t async_loop_run_until_idle(async_loop_t* loop);
+
+// Quits the message loop.
+// Active invocations of |async_loop_run()| and threads started using
+// |async_loop_start_thread()| will eventually terminate upon completion of their
+// current unit of work.
+//
+// Subsequent calls to |async_loop_run()| or |async_loop_start_thread()|
+// will return immediately until |async_loop_reset_quit()| is called.
+void async_loop_quit(async_loop_t* loop);
+
+// Resets the quit state of the message loop so that it can be restarted
+// using |async_loop_run()| or |async_loop_start_thread()|.
+//
+// This function must only be called when the message loop is not running.
+// The caller must ensure all active invocations of |async_loop_run()| and
+// threads started using |async_loop_start_thread()| have terminated before
+// resetting the quit state.
+//
+// Returns |ZX_OK| if the loop's state was |ASYNC_LOOP_RUNNABLE| or |ASYNC_LOOP_QUIT|.
+// Returns |ZX_ERR_BAD_STATE| if the loop's state was |ASYNC_LOOP_SHUTDOWN| or if
+// the message loop is currently active on one or more threads.
+zx_status_t async_loop_reset_quit(async_loop_t* loop);
+
+// Returns the current state of the message loop.
+typedef uint32_t async_loop_state_t;
+#define ASYNC_LOOP_RUNNABLE ((async_loop_state_t) 0)
+#define ASYNC_LOOP_QUIT ((async_loop_state_t) 1)
+#define ASYNC_LOOP_SHUTDOWN ((async_loop_state_t) 2)
+async_loop_state_t async_loop_get_state(async_loop_t* loop);
+
+// Starts a message loop running on a new thread.
+// The thread will run until the loop quits.
+//
+// |name| is the desired name for the new thread, may be NULL.
+// If |out_thread| is not NULL, it is set to the new thread identifier.
+//
+// Returns |ZX_OK| on success.
+// Returns |ZX_ERR_BAD_STATE| if the loop was shut down with |async_loop_shutdown()|.
+// Returns |ZX_ERR_NO_MEMORY| if allocation or thread creation failed.
+zx_status_t async_loop_start_thread(async_loop_t* loop, const char* name,
+ thrd_t* out_thread);
+
+// Blocks until all dispatch threads started with |async_loop_start_thread()|
+// have terminated.
+void async_loop_join_threads(async_loop_t* loop);
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_LOOP_LOOP_H_
\ No newline at end of file
diff --git a/pkg/async-loop/loop.c b/pkg/async-loop/loop.c
new file mode 100644
index 0000000..e70627a
--- /dev/null
+++ b/pkg/async-loop/loop.c
@@ -0,0 +1,797 @@
+// Copyright 2017 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.
+
+#ifndef _ALL_SOURCE
+#define _ALL_SOURCE // Enables thrd_create_with_name in <threads.h>.
+#endif
+#include <lib/async-loop/loop.h>
+
+#include <assert.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+
+#include <zircon/assert.h>
+#include <zircon/listnode.h>
+#include <zircon/syscalls.h>
+#include <zircon/syscalls/hypervisor.h>
+
+#include <lib/async/default.h>
+#include <lib/async/exception.h>
+#include <lib/async/receiver.h>
+#include <lib/async/task.h>
+#include <lib/async/trap.h>
+#include <lib/async/wait.h>
+
+// The port wait key associated with the dispatcher's control messages.
+#define KEY_CONTROL (0u)
+
+static zx_time_t async_loop_now(async_dispatcher_t* dispatcher);
+static zx_status_t async_loop_begin_wait(async_dispatcher_t* dispatcher, async_wait_t* wait);
+static zx_status_t async_loop_cancel_wait(async_dispatcher_t* dispatcher, async_wait_t* wait);
+static zx_status_t async_loop_post_task(async_dispatcher_t* dispatcher, async_task_t* task);
+static zx_status_t async_loop_cancel_task(async_dispatcher_t* dispatcher, async_task_t* task);
+static zx_status_t async_loop_queue_packet(async_dispatcher_t* dispatcher, async_receiver_t* receiver,
+ const zx_packet_user_t* data);
+static zx_status_t async_loop_set_guest_bell_trap(
+ async_dispatcher_t* dispatcher, async_guest_bell_trap_t* trap,
+ zx_handle_t guest, zx_vaddr_t addr, size_t length);
+static zx_status_t async_loop_bind_exception_port(async_dispatcher_t* async,
+ async_exception_t* exception);
+static zx_status_t async_loop_unbind_exception_port(async_dispatcher_t* async,
+ async_exception_t* exception);
+static zx_status_t async_loop_resume_from_exception(async_dispatcher_t* async,
+ async_exception_t* exception,
+ zx_handle_t task,
+ uint32_t options);
+
+static const async_ops_t async_loop_ops = {
+ .version = ASYNC_OPS_V2,
+ .reserved = 0,
+ .v1 = {
+ .now = async_loop_now,
+ .begin_wait = async_loop_begin_wait,
+ .cancel_wait = async_loop_cancel_wait,
+ .post_task = async_loop_post_task,
+ .cancel_task = async_loop_cancel_task,
+ .queue_packet = async_loop_queue_packet,
+ .set_guest_bell_trap = async_loop_set_guest_bell_trap,
+ },
+ .v2 = {
+ .bind_exception_port = async_loop_bind_exception_port,
+ .unbind_exception_port = async_loop_unbind_exception_port,
+ .resume_from_exception = async_loop_resume_from_exception,
+ },
+};
+
+typedef struct thread_record {
+ list_node_t node;
+ thrd_t thread;
+} thread_record_t;
+
+const async_loop_config_t kAsyncLoopConfigAttachToThread = {
+ .make_default_for_current_thread = true};
+const async_loop_config_t kAsyncLoopConfigNoAttachToThread = {
+ .make_default_for_current_thread = false};
+
+typedef struct async_loop {
+ async_dispatcher_t dispatcher; // must be first (the loop inherits from async_dispatcher_t)
+ async_loop_config_t config; // immutable
+ zx_handle_t port; // immutable
+ zx_handle_t timer; // immutable
+
+ _Atomic async_loop_state_t state;
+ atomic_uint active_threads; // number of active dispatch threads
+
+ mtx_t lock; // guards the lists and the dispatching tasks flag
+ bool dispatching_tasks; // true while the loop is busy dispatching tasks
+ list_node_t wait_list; // most recently added first
+ list_node_t task_list; // pending tasks, earliest deadline first
+ list_node_t due_list; // due tasks, earliest deadline first
+ list_node_t thread_list; // earliest created thread first
+ list_node_t exception_list; // most recently added first
+} async_loop_t;
+
+static zx_status_t async_loop_run_once(async_loop_t* loop, zx_time_t deadline);
+static zx_status_t async_loop_dispatch_wait(async_loop_t* loop, async_wait_t* wait,
+ zx_status_t status, const zx_packet_signal_t* signal);
+static zx_status_t async_loop_dispatch_tasks(async_loop_t* loop);
+static void async_loop_dispatch_task(async_loop_t* loop, async_task_t* task,
+ zx_status_t status);
+static zx_status_t async_loop_dispatch_packet(async_loop_t* loop, async_receiver_t* receiver,
+ zx_status_t status, const zx_packet_user_t* data);
+static zx_status_t async_loop_dispatch_guest_bell_trap(async_loop_t* loop,
+ async_guest_bell_trap_t* trap,
+ zx_status_t status,
+ const zx_packet_guest_bell_t* bell);
+static zx_status_t async_loop_dispatch_exception(async_loop_t* loop,
+ async_exception_t* exception,
+ zx_status_t status,
+ const zx_port_packet_t* report);
+static void async_loop_wake_threads(async_loop_t* loop);
+static void async_loop_insert_task_locked(async_loop_t* loop, async_task_t* task);
+static void async_loop_restart_timer_locked(async_loop_t* loop);
+static void async_loop_invoke_prologue(async_loop_t* loop);
+static void async_loop_invoke_epilogue(async_loop_t* loop);
+
+static_assert(sizeof(list_node_t) <= sizeof(async_state_t),
+ "async_state_t too small");
+
+#define TO_NODE(type, ptr) ((list_node_t*)&ptr->state)
+#define FROM_NODE(type, ptr) ((type*)((char*)(ptr)-offsetof(type, state)))
+
+static inline list_node_t* wait_to_node(async_wait_t* wait) {
+ return TO_NODE(async_wait_t, wait);
+}
+
+static inline async_wait_t* node_to_wait(list_node_t* node) {
+ return FROM_NODE(async_wait_t, node);
+}
+
+static inline list_node_t* task_to_node(async_task_t* task) {
+ return TO_NODE(async_task_t, task);
+}
+
+static inline async_task_t* node_to_task(list_node_t* node) {
+ return FROM_NODE(async_task_t, node);
+}
+
+static inline list_node_t* exception_to_node(async_exception_t* exception) {
+ return TO_NODE(async_exception_t, exception);
+}
+
+static inline async_exception_t* node_to_exception(list_node_t* node) {
+ return FROM_NODE(async_exception_t, node);
+}
+
+zx_status_t async_loop_create(const async_loop_config_t* config, async_loop_t** out_loop) {
+ ZX_DEBUG_ASSERT(out_loop);
+ ZX_DEBUG_ASSERT(config != NULL);
+
+ async_loop_t* loop = calloc(1u, sizeof(async_loop_t));
+ if (!loop)
+ return ZX_ERR_NO_MEMORY;
+ atomic_init(&loop->state, ASYNC_LOOP_RUNNABLE);
+ atomic_init(&loop->active_threads, 0u);
+
+ loop->dispatcher.ops = &async_loop_ops;
+ loop->config = *config;
+ mtx_init(&loop->lock, mtx_plain);
+ list_initialize(&loop->wait_list);
+ list_initialize(&loop->task_list);
+ list_initialize(&loop->due_list);
+ list_initialize(&loop->thread_list);
+ list_initialize(&loop->exception_list);
+
+ zx_status_t status = zx_port_create(0u, &loop->port);
+ if (status == ZX_OK)
+ status = zx_timer_create(0u, ZX_CLOCK_MONOTONIC, &loop->timer);
+ if (status == ZX_OK) {
+ status = zx_object_wait_async(loop->timer, loop->port, KEY_CONTROL,
+ ZX_TIMER_SIGNALED,
+ ZX_WAIT_ASYNC_REPEATING);
+ }
+ if (status == ZX_OK) {
+ *out_loop = loop;
+ if (loop->config.make_default_for_current_thread) {
+ ZX_DEBUG_ASSERT(async_get_default_dispatcher() == NULL);
+ async_set_default_dispatcher(&loop->dispatcher);
+ }
+ } else {
+ loop->config.make_default_for_current_thread = false;
+ async_loop_destroy(loop);
+ }
+ return status;
+}
+
+void async_loop_destroy(async_loop_t* loop) {
+ ZX_DEBUG_ASSERT(loop);
+
+ async_loop_shutdown(loop);
+
+ zx_handle_close(loop->port);
+ zx_handle_close(loop->timer);
+ mtx_destroy(&loop->lock);
+ free(loop);
+}
+
+void async_loop_shutdown(async_loop_t* loop) {
+ ZX_DEBUG_ASSERT(loop);
+
+ async_loop_state_t prior_state =
+ atomic_exchange_explicit(&loop->state, ASYNC_LOOP_SHUTDOWN,
+ memory_order_acq_rel);
+ if (prior_state == ASYNC_LOOP_SHUTDOWN)
+ return;
+
+ async_loop_wake_threads(loop);
+ async_loop_join_threads(loop);
+
+ list_node_t* node;
+ while ((node = list_remove_head(&loop->wait_list))) {
+ async_wait_t* wait = node_to_wait(node);
+ async_loop_dispatch_wait(loop, wait, ZX_ERR_CANCELED, NULL);
+ }
+ while ((node = list_remove_head(&loop->due_list))) {
+ async_task_t* task = node_to_task(node);
+ async_loop_dispatch_task(loop, task, ZX_ERR_CANCELED);
+ }
+ while ((node = list_remove_head(&loop->task_list))) {
+ async_task_t* task = node_to_task(node);
+ async_loop_dispatch_task(loop, task, ZX_ERR_CANCELED);
+ }
+ while ((node = list_remove_head(&loop->exception_list))) {
+ async_exception_t* exception = node_to_exception(node);
+ async_loop_dispatch_exception(loop, exception, ZX_ERR_CANCELED, NULL);
+ }
+
+ if (loop->config.make_default_for_current_thread) {
+ ZX_DEBUG_ASSERT(async_get_default_dispatcher() == &loop->dispatcher);
+ async_set_default_dispatcher(NULL);
+ }
+}
+
+zx_status_t async_loop_run(async_loop_t* loop, zx_time_t deadline, bool once) {
+ ZX_DEBUG_ASSERT(loop);
+
+ zx_status_t status;
+ atomic_fetch_add_explicit(&loop->active_threads, 1u, memory_order_acq_rel);
+ do {
+ status = async_loop_run_once(loop, deadline);
+ } while (status == ZX_OK && !once);
+ atomic_fetch_sub_explicit(&loop->active_threads, 1u, memory_order_acq_rel);
+ return status;
+}
+
+zx_status_t async_loop_run_until_idle(async_loop_t* loop) {
+ zx_status_t status = async_loop_run(loop, 0, false);
+ if (status == ZX_ERR_TIMED_OUT) {
+ status = ZX_OK;
+ }
+ return status;
+}
+
+static zx_status_t async_loop_run_once(async_loop_t* loop, zx_time_t deadline) {
+ async_loop_state_t state = atomic_load_explicit(&loop->state, memory_order_acquire);
+ if (state == ASYNC_LOOP_SHUTDOWN)
+ return ZX_ERR_BAD_STATE;
+ if (state != ASYNC_LOOP_RUNNABLE)
+ return ZX_ERR_CANCELED;
+
+ zx_port_packet_t packet;
+ zx_status_t status = zx_port_wait(loop->port, deadline, &packet);
+ if (status != ZX_OK)
+ return status;
+
+ if (packet.key == KEY_CONTROL) {
+ // Handle wake-up packets.
+ if (packet.type == ZX_PKT_TYPE_USER)
+ return ZX_OK;
+
+ // Handle task timer expirations.
+ if (packet.type == ZX_PKT_TYPE_SIGNAL_REP &&
+ packet.signal.observed & ZX_TIMER_SIGNALED) {
+ return async_loop_dispatch_tasks(loop);
+ }
+ } else {
+ // Handle wait completion packets.
+ if (packet.type == ZX_PKT_TYPE_SIGNAL_ONE) {
+ async_wait_t* wait = (void*)(uintptr_t)packet.key;
+ mtx_lock(&loop->lock);
+ list_delete(wait_to_node(wait));
+ mtx_unlock(&loop->lock);
+ return async_loop_dispatch_wait(loop, wait, packet.status, &packet.signal);
+ }
+
+ // Handle queued user packets.
+ if (packet.type == ZX_PKT_TYPE_USER) {
+ async_receiver_t* receiver = (void*)(uintptr_t)packet.key;
+ return async_loop_dispatch_packet(loop, receiver, packet.status, &packet.user);
+ }
+
+ // Handle guest bell trap packets.
+ if (packet.type == ZX_PKT_TYPE_GUEST_BELL) {
+ async_guest_bell_trap_t* trap = (void*)(uintptr_t)packet.key;
+ return async_loop_dispatch_guest_bell_trap(
+ loop, trap, packet.status, &packet.guest_bell);
+ }
+
+ // Handle exception packets.
+ if (ZX_PKT_IS_EXCEPTION(packet.type)) {
+ async_exception_t* exception = (void*)(uintptr_t)packet.key;
+ return async_loop_dispatch_exception(loop, exception, packet.status,
+ &packet);
+ }
+ }
+
+ ZX_DEBUG_ASSERT(false);
+ return ZX_ERR_INTERNAL;
+}
+
+async_dispatcher_t* async_loop_get_dispatcher(async_loop_t* loop) {
+ // Note: The loop's implementation inherits from async_t so we can upcast to it.
+ return (async_dispatcher_t*)loop;
+}
+
+async_loop_t* async_loop_from_dispatcher(async_dispatcher_t* async) {
+ return (async_loop_t*)async;
+}
+
+static zx_status_t async_loop_dispatch_guest_bell_trap(async_loop_t* loop,
+ async_guest_bell_trap_t* trap,
+ zx_status_t status,
+ const zx_packet_guest_bell_t* bell) {
+ async_loop_invoke_prologue(loop);
+ trap->handler((async_dispatcher_t*)loop, trap, status, bell);
+ async_loop_invoke_epilogue(loop);
+ return ZX_OK;
+}
+
+static zx_status_t async_loop_dispatch_wait(async_loop_t* loop, async_wait_t* wait,
+ zx_status_t status, const zx_packet_signal_t* signal) {
+ async_loop_invoke_prologue(loop);
+ wait->handler((async_dispatcher_t*)loop, wait, status, signal);
+ async_loop_invoke_epilogue(loop);
+ return ZX_OK;
+}
+
+static zx_status_t async_loop_dispatch_tasks(async_loop_t* loop) {
+ // Dequeue and dispatch one task at a time in case an earlier task wants
+ // to cancel a later task which has also come due. At most one thread
+ // can dispatch tasks at any given moment (to preserve serial ordering).
+ // Timer restarts are suppressed until we run out of tasks to dispatch.
+ mtx_lock(&loop->lock);
+ if (!loop->dispatching_tasks) {
+ loop->dispatching_tasks = true;
+
+ // Extract all of the tasks that are due into |due_list| for dispatch
+ // unless we already have some waiting from a previous iteration which
+ // we would like to process in order.
+ list_node_t* node;
+ if (list_is_empty(&loop->due_list)) {
+ zx_time_t due_time = async_loop_now((async_dispatcher_t*)loop);
+ list_node_t* tail = NULL;
+ list_for_every(&loop->task_list, node) {
+ if (node_to_task(node)->deadline > due_time)
+ break;
+ tail = node;
+ }
+ if (tail) {
+ list_node_t* head = loop->task_list.next;
+ loop->task_list.next = tail->next;
+ tail->next->prev = &loop->task_list;
+ loop->due_list.next = head;
+ head->prev = &loop->due_list;
+ loop->due_list.prev = tail;
+ tail->next = &loop->due_list;
+ }
+ }
+
+ // Dispatch all due tasks. Note that they might be canceled concurrently
+ // so we need to grab the lock during each iteration to fetch the next
+ // item from the list.
+ while ((node = list_remove_head(&loop->due_list))) {
+ mtx_unlock(&loop->lock);
+
+ // Invoke the handler. Note that it might destroy itself.
+ async_task_t* task = node_to_task(node);
+ async_loop_dispatch_task(loop, task, ZX_OK);
+
+ mtx_lock(&loop->lock);
+ async_loop_state_t state = atomic_load_explicit(&loop->state, memory_order_acquire);
+ if (state != ASYNC_LOOP_RUNNABLE)
+ break;
+ }
+
+ loop->dispatching_tasks = false;
+ async_loop_restart_timer_locked(loop);
+ }
+ mtx_unlock(&loop->lock);
+ return ZX_OK;
+}
+
+static void async_loop_dispatch_task(async_loop_t* loop,
+ async_task_t* task,
+ zx_status_t status) {
+ // Invoke the handler. Note that it might destroy itself.
+ async_loop_invoke_prologue(loop);
+ task->handler((async_dispatcher_t*)loop, task, status);
+ async_loop_invoke_epilogue(loop);
+}
+
+static zx_status_t async_loop_dispatch_packet(async_loop_t* loop, async_receiver_t* receiver,
+ zx_status_t status, const zx_packet_user_t* data) {
+ // Invoke the handler. Note that it might destroy itself.
+ async_loop_invoke_prologue(loop);
+ receiver->handler((async_dispatcher_t*)loop, receiver, status, data);
+ async_loop_invoke_epilogue(loop);
+ return ZX_OK;
+}
+
+static zx_status_t async_loop_dispatch_exception(async_loop_t* loop,
+ async_exception_t* exception,
+ zx_status_t status,
+ const zx_port_packet_t* report) {
+ // Invoke the handler. Note that it might destroy itself.
+ async_loop_invoke_prologue(loop);
+ exception->handler((async_dispatcher_t*)loop, exception, status, report);
+ async_loop_invoke_epilogue(loop);
+ return ZX_OK;
+}
+
+void async_loop_quit(async_loop_t* loop) {
+ ZX_DEBUG_ASSERT(loop);
+
+ async_loop_state_t expected_state = ASYNC_LOOP_RUNNABLE;
+ if (!atomic_compare_exchange_strong_explicit(&loop->state, &expected_state,
+ ASYNC_LOOP_QUIT,
+ memory_order_acq_rel, memory_order_acquire))
+ return;
+
+ async_loop_wake_threads(loop);
+}
+
+static void async_loop_wake_threads(async_loop_t* loop) {
+ // Queue enough packets to awaken all active threads.
+ // This is safe because any new threads which join the pool first increment the
+ // active thread count then check the loop state, so the count we observe here
+ // cannot be less than the number of threads which might be blocked in |port_wait|.
+ // Issuing too many packets is also harmless.
+ uint32_t n = atomic_load_explicit(&loop->active_threads, memory_order_acquire);
+ for (uint32_t i = 0u; i < n; i++) {
+ zx_port_packet_t packet = {
+ .key = KEY_CONTROL,
+ .type = ZX_PKT_TYPE_USER,
+ .status = ZX_OK};
+ zx_status_t status = zx_port_queue(loop->port, &packet);
+ ZX_ASSERT_MSG(status == ZX_OK, "zx_port_queue: status=%d", status);
+ }
+}
+
+zx_status_t async_loop_reset_quit(async_loop_t* loop) {
+ ZX_DEBUG_ASSERT(loop);
+
+ // Ensure that there are no active threads before resetting the quit state.
+ // This check is inherently racy but not dangerously so. It's mainly a
+ // sanity check for client code so we can make a stronger statement about
+ // how |async_loop_reset_quit()| is supposed to be used.
+ uint32_t n = atomic_load_explicit(&loop->active_threads, memory_order_acquire);
+ if (n != 0)
+ return ZX_ERR_BAD_STATE;
+
+ async_loop_state_t expected_state = ASYNC_LOOP_QUIT;
+ if (atomic_compare_exchange_strong_explicit(&loop->state, &expected_state,
+ ASYNC_LOOP_RUNNABLE,
+ memory_order_acq_rel, memory_order_acquire)) {
+ return ZX_OK;
+ }
+
+ async_loop_state_t state = atomic_load_explicit(&loop->state, memory_order_acquire);
+ if (state == ASYNC_LOOP_RUNNABLE)
+ return ZX_OK;
+ return ZX_ERR_BAD_STATE;
+}
+
+async_loop_state_t async_loop_get_state(async_loop_t* loop) {
+ ZX_DEBUG_ASSERT(loop);
+
+ return atomic_load_explicit(&loop->state, memory_order_acquire);
+}
+
+zx_time_t async_loop_now(async_dispatcher_t* dispatcher) {
+ return zx_clock_get_monotonic();
+}
+
+static zx_status_t async_loop_begin_wait(async_dispatcher_t* async, async_wait_t* wait) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(wait);
+
+ if (atomic_load_explicit(&loop->state, memory_order_acquire) == ASYNC_LOOP_SHUTDOWN)
+ return ZX_ERR_BAD_STATE;
+
+ mtx_lock(&loop->lock);
+
+ zx_status_t status = zx_object_wait_async(
+ wait->object, loop->port, (uintptr_t)wait, wait->trigger, ZX_WAIT_ASYNC_ONCE);
+ if (status == ZX_OK) {
+ list_add_head(&loop->wait_list, wait_to_node(wait));
+ } else {
+ ZX_ASSERT_MSG(status == ZX_ERR_ACCESS_DENIED,
+ "zx_object_wait_async: status=%d", status);
+ }
+
+ mtx_unlock(&loop->lock);
+ return status;
+}
+
+static zx_status_t async_loop_cancel_wait(async_dispatcher_t* async, async_wait_t* wait) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(wait);
+
+ // Note: We need to process cancelations even while the loop is being
+ // destroyed in case the client is counting on the handler not being
+ // invoked again past this point.
+
+ mtx_lock(&loop->lock);
+
+ // First, confirm that the wait is actually pending.
+ list_node_t* node = wait_to_node(wait);
+ if (!list_in_list(node)) {
+ mtx_unlock(&loop->lock);
+ return ZX_ERR_NOT_FOUND;
+ }
+
+ // Next, cancel the wait. This may be racing with another thread that
+ // has read the wait's packet but not yet dispatched it. So if we fail
+ // to cancel then we assume we lost the race.
+ zx_status_t status = zx_port_cancel(loop->port, wait->object,
+ (uintptr_t)wait);
+ if (status == ZX_OK) {
+ list_delete(node);
+ } else {
+ ZX_ASSERT_MSG(status == ZX_ERR_NOT_FOUND,
+ "zx_port_cancel: status=%d", status);
+ }
+
+ mtx_unlock(&loop->lock);
+ return status;
+}
+
+static zx_status_t async_loop_post_task(async_dispatcher_t* async, async_task_t* task) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(task);
+
+ if (atomic_load_explicit(&loop->state, memory_order_acquire) == ASYNC_LOOP_SHUTDOWN)
+ return ZX_ERR_BAD_STATE;
+
+ mtx_lock(&loop->lock);
+
+ async_loop_insert_task_locked(loop, task);
+ if (!loop->dispatching_tasks &&
+ task_to_node(task)->prev == &loop->task_list) {
+ // Task inserted at head. Earliest deadline changed.
+ async_loop_restart_timer_locked(loop);
+ }
+
+ mtx_unlock(&loop->lock);
+ return ZX_OK;
+}
+
+static zx_status_t async_loop_cancel_task(async_dispatcher_t* async, async_task_t* task) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(task);
+
+ // Note: We need to process cancelations even while the loop is being
+ // destroyed in case the client is counting on the handler not being
+ // invoked again past this point. Also, the task we're removing here
+ // might be present in the dispatcher's |due_list| if it is pending
+ // dispatch instead of in the loop's |task_list| as usual. The same
+ // logic works in both cases.
+
+ mtx_lock(&loop->lock);
+ list_node_t* node = task_to_node(task);
+ if (!list_in_list(node)) {
+ mtx_unlock(&loop->lock);
+ return ZX_ERR_NOT_FOUND;
+ }
+
+ // Determine whether the head task was canceled and following task has
+ // a later deadline. If so, we will bump the timer along to that deadline.
+ bool must_restart = !loop->dispatching_tasks &&
+ node->prev == &loop->task_list &&
+ node->next != &loop->task_list &&
+ node_to_task(node->next)->deadline > task->deadline;
+ list_delete(node);
+ if (must_restart)
+ async_loop_restart_timer_locked(loop);
+
+ mtx_unlock(&loop->lock);
+ return ZX_OK;
+}
+
+static zx_status_t async_loop_queue_packet(async_dispatcher_t* async, async_receiver_t* receiver,
+ const zx_packet_user_t* data) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(receiver);
+
+ if (atomic_load_explicit(&loop->state, memory_order_acquire) == ASYNC_LOOP_SHUTDOWN)
+ return ZX_ERR_BAD_STATE;
+
+ zx_port_packet_t packet = {
+ .key = (uintptr_t)receiver,
+ .type = ZX_PKT_TYPE_USER,
+ .status = ZX_OK};
+ if (data)
+ packet.user = *data;
+ return zx_port_queue(loop->port, &packet);
+}
+
+static zx_status_t async_loop_set_guest_bell_trap(
+ async_dispatcher_t* async, async_guest_bell_trap_t* trap,
+ zx_handle_t guest, zx_vaddr_t addr, size_t length) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(trap);
+
+ if (atomic_load_explicit(&loop->state, memory_order_acquire) == ASYNC_LOOP_SHUTDOWN)
+ return ZX_ERR_BAD_STATE;
+
+ zx_status_t status = zx_guest_set_trap(guest, ZX_GUEST_TRAP_BELL, addr,
+ length, loop->port, (uintptr_t)trap);
+ if (status != ZX_OK) {
+ ZX_ASSERT_MSG(status == ZX_ERR_ACCESS_DENIED ||
+ status == ZX_ERR_ALREADY_EXISTS ||
+ status == ZX_ERR_INVALID_ARGS ||
+ status == ZX_ERR_OUT_OF_RANGE ||
+ status == ZX_ERR_WRONG_TYPE,
+ "zx_guest_set_trap: status=%d", status);
+ }
+ return status;
+}
+
+static zx_status_t async_loop_bind_exception_port(async_dispatcher_t* async,
+ async_exception_t* exception) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(exception);
+
+ if (atomic_load_explicit(&loop->state, memory_order_acquire) == ASYNC_LOOP_SHUTDOWN)
+ return ZX_ERR_BAD_STATE;
+
+ mtx_lock(&loop->lock);
+
+ uint64_t key = (uintptr_t)(void*) exception;
+ zx_status_t status = zx_task_bind_exception_port(exception->task, loop->port,
+ key, exception->options);
+ if (status == ZX_OK) {
+ list_add_head(&loop->exception_list, exception_to_node(exception));
+ }
+
+ mtx_unlock(&loop->lock);
+ return status;
+}
+
+static zx_status_t async_loop_unbind_exception_port(async_dispatcher_t* async,
+ async_exception_t* exception) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(exception);
+
+ // Note: We need to process unbindings even while the loop is being
+ // destroyed in case the client is counting on the handler not being
+ // invoked again past this point.
+
+ mtx_lock(&loop->lock);
+
+ // First, confirm that the port is actually bound.
+ list_node_t* node = exception_to_node(exception);
+ if (!list_in_list(node)) {
+ mtx_unlock(&loop->lock);
+ return ZX_ERR_NOT_FOUND;
+ }
+
+ uint64_t key = (uintptr_t)(void*) exception;
+ zx_status_t status = zx_task_bind_exception_port(exception->task,
+ ZX_HANDLE_INVALID, key, 0);
+
+ if (status == ZX_OK) {
+ list_delete(node);
+ }
+
+ mtx_unlock(&loop->lock);
+ return status;
+}
+
+static zx_status_t async_loop_resume_from_exception(async_dispatcher_t* async,
+ async_exception_t* exception,
+ zx_handle_t task,
+ uint32_t options) {
+ async_loop_t* loop = (async_loop_t*)async;
+ ZX_DEBUG_ASSERT(loop);
+ ZX_DEBUG_ASSERT(exception);
+
+ if (atomic_load_explicit(&loop->state, memory_order_acquire) == ASYNC_LOOP_SHUTDOWN)
+ return ZX_ERR_BAD_STATE;
+
+ return zx_task_resume_from_exception(task, loop->port, options);
+}
+
+static void async_loop_insert_task_locked(async_loop_t* loop, async_task_t* task) {
+ // TODO(ZX-976): We assume that tasks are inserted in quasi-monotonic order and
+ // that insertion into the task queue will typically take no more than a few steps.
+ // If this assumption proves false and the cost of insertion becomes a problem, we
+ // should consider using a more efficient representation for maintaining order.
+ list_node_t* node;
+ for (node = loop->task_list.prev; node != &loop->task_list; node = node->prev) {
+ if (task->deadline >= node_to_task(node)->deadline)
+ break;
+ }
+ list_add_after(node, task_to_node(task));
+}
+
+static void async_loop_restart_timer_locked(async_loop_t* loop) {
+ zx_time_t deadline;
+ if (list_is_empty(&loop->due_list)) {
+ list_node_t* head = list_peek_head(&loop->task_list);
+ if (!head)
+ return;
+ async_task_t* task = node_to_task(head);
+ deadline = task->deadline;
+ if (deadline == ZX_TIME_INFINITE)
+ return;
+ } else {
+ // Fire now.
+ deadline = 0ULL;
+ }
+
+ zx_status_t status = zx_timer_set(loop->timer, deadline, 0);
+ ZX_ASSERT_MSG(status == ZX_OK, "zx_timer_set: status=%d", status);
+}
+
+static void async_loop_invoke_prologue(async_loop_t* loop) {
+ if (loop->config.prologue)
+ loop->config.prologue(loop, loop->config.data);
+}
+
+static void async_loop_invoke_epilogue(async_loop_t* loop) {
+ if (loop->config.epilogue)
+ loop->config.epilogue(loop, loop->config.data);
+}
+
+static int async_loop_run_thread(void* data) {
+ async_loop_t* loop = (async_loop_t*)data;
+ async_set_default_dispatcher(&loop->dispatcher);
+ async_loop_run(loop, ZX_TIME_INFINITE, false);
+ return 0;
+}
+
+zx_status_t async_loop_start_thread(async_loop_t* loop, const char* name, thrd_t* out_thread) {
+ ZX_DEBUG_ASSERT(loop);
+
+ // This check is inherently racy. The client should not be racing shutdown
+ // with attemps to start new threads. This is mainly a sanity check.
+ async_loop_state_t state = atomic_load_explicit(&loop->state, memory_order_acquire);
+ if (state == ASYNC_LOOP_SHUTDOWN)
+ return ZX_ERR_BAD_STATE;
+
+ thread_record_t* rec = calloc(1u, sizeof(thread_record_t));
+ if (!rec)
+ return ZX_ERR_NO_MEMORY;
+
+ if (thrd_create_with_name(&rec->thread, async_loop_run_thread, loop, name) != thrd_success) {
+ free(rec);
+ return ZX_ERR_NO_MEMORY;
+ }
+
+ mtx_lock(&loop->lock);
+ list_add_tail(&loop->thread_list, &rec->node);
+ mtx_unlock(&loop->lock);
+
+ if (out_thread)
+ *out_thread = rec->thread;
+ return ZX_OK;
+}
+
+void async_loop_join_threads(async_loop_t* loop) {
+ ZX_DEBUG_ASSERT(loop);
+
+ mtx_lock(&loop->lock);
+ for (;;) {
+ thread_record_t* rec = (thread_record_t*)list_remove_head(&loop->thread_list);
+ if (!rec)
+ break;
+
+ mtx_unlock(&loop->lock);
+ thrd_t thread = rec->thread;
+ free(rec);
+ int result = thrd_join(thread, NULL);
+ ZX_DEBUG_ASSERT(result == thrd_success);
+ mtx_lock(&loop->lock);
+ }
+ mtx_unlock(&loop->lock);
+}
diff --git a/pkg/async-loop/meta.json b/pkg/async-loop/meta.json
new file mode 100644
index 0000000..8a1eef7
--- /dev/null
+++ b/pkg/async-loop/meta.json
@@ -0,0 +1,22 @@
+{
+ "banjo_deps": [],
+ "deps": [
+ "async",
+ "async-default"
+ ],
+ "fidl_deps": [],
+ "files": [
+ "pkg/async-loop/loop.c",
+ "pkg/async-loop/include/lib/async-loop/loop.h"
+ ],
+ "headers": [
+ "pkg/async-loop/include/lib/async-loop/loop.h"
+ ],
+ "include_dir": "pkg/async-loop/include",
+ "name": "async-loop",
+ "root": "pkg/async-loop",
+ "sources": [
+ "pkg/async-loop/loop.c"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/async/include/lib/async/dispatcher.h b/pkg/async/include/lib/async/dispatcher.h
new file mode 100644
index 0000000..ff9b760
--- /dev/null
+++ b/pkg/async/include/lib/async/dispatcher.h
@@ -0,0 +1,125 @@
+// Copyright 2017 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.
+
+#ifndef LIB_ASYNC_DISPATCHER_H_
+#define LIB_ASYNC_DISPATCHER_H_
+
+#include <zircon/compiler.h>
+#include <zircon/syscalls/port.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+// Dispatcher interface for performing asynchronous operations.
+// There may be multiple implementations of this interface.
+typedef struct async_dispatcher async_dispatcher_t;
+
+// Forward declarations for asynchronous operation structures.
+typedef struct async_guest_bell_trap async_guest_bell_trap_t;
+typedef struct async_wait async_wait_t;
+typedef struct async_task async_task_t;
+typedef struct async_receiver async_receiver_t;
+typedef struct async_exception async_exception_t;
+
+// Private state owned by the asynchronous dispatcher.
+// This allows the dispatcher to associate a small amount of state with pending
+// asynchronous operations without having to allocate additional heap storage of
+// its own.
+//
+// Clients must initialize the contents of this structure to zero using
+// |ASYNC_STATE_INIT| or with calloc, memset, or a similar means.
+typedef struct {
+ uintptr_t reserved[2];
+} async_state_t;
+
+#define ASYNC_STATE_INIT \
+ { 0u, 0u }
+
+// Asynchronous dispatcher interface.
+//
+// Clients should not call into this interface directly: use the wrapper functions
+// declared in other header files, such as |async_begin_wait()| in <lib/async/wait.h>.
+// See the documentation of those functions for details about each method's purpose
+// and behavior.
+//
+// This interface consists of several groups of methods:
+//
+// - Timing: |now|
+// - Waiting for signals: |begin_wait|, |cancel_wait|
+// - Posting tasks: |post_task|, |cancel_task|
+// - Queuing packets: |queue_packet|
+// - Virtual machine operations: |set_guest_bell_trap|
+// - Exception handling: |bind_exception_port|, |unbind_exception_port|
+//
+// To preserve binary compatibility, each successive version of this interface
+// is guaranteed to be backwards-compatible with clients of earlier versions.
+// New methods must only be added by extending the structure at the end and
+// declaring a new version number. Do not reorder the declarations or modify
+// existing versions.
+//
+// Implementations of this interface must provide valid (non-null) function pointers
+// for every method declared in the interface version they support. Unsupported
+// methods must return |ZX_ERR_NOT_SUPPORTED| and have no other side-effects.
+// Furthermore, if an implementation supports one method of a group, such as |begin_wait|,
+// then it must also support the other methods of the group, such as |cancel_wait|.
+//
+// Many clients assume that the dispatcher interface is fully implemented and may
+// fail to work with dispatchers that do not support the methods they need.
+// Therefore general-purpose dispatcher implementations are encouraged to support
+// the whole interface to ensure broad compatibility.
+typedef uint32_t async_ops_version_t;
+
+#define ASYNC_OPS_V1 ((async_ops_version_t) 1)
+#define ASYNC_OPS_V2 ((async_ops_version_t) 2)
+
+typedef struct async_ops {
+ // The interface version number, e.g. |ASYNC_OPS_V2|.
+ async_ops_version_t version;
+
+ // Reserved for future expansion, set to zero.
+ uint32_t reserved;
+
+ // Operations supported by |ASYNC_OPS_V1|.
+ struct v1 {
+ // See |async_now()| for details.
+ zx_time_t (*now)(async_dispatcher_t* dispatcher);
+ // See |async_begin_wait()| for details.
+ zx_status_t (*begin_wait)(async_dispatcher_t* dispatcher, async_wait_t* wait);
+ // See |async_cancel_wait()| for details.
+ zx_status_t (*cancel_wait)(async_dispatcher_t* dispatcher, async_wait_t* wait);
+ // See |async_post_task()| for details.
+ zx_status_t (*post_task)(async_dispatcher_t* dispatcher, async_task_t* task);
+ // See |async_cancel_task()| for details.
+ zx_status_t (*cancel_task)(async_dispatcher_t* dispatcher, async_task_t* task);
+ // See |async_queue_packet()| for details.
+ zx_status_t (*queue_packet)(async_dispatcher_t* dispatcher, async_receiver_t* receiver,
+ const zx_packet_user_t* data);
+ // See |async_set_guest_bell_trap()| for details.
+ zx_status_t (*set_guest_bell_trap)(async_dispatcher_t* dispatcher, async_guest_bell_trap_t* trap,
+ zx_handle_t guest, zx_vaddr_t addr, size_t length);
+ } v1;
+
+ // Operations supported by |ASYNC_OPS_V2|, in addition to those in V1.
+ struct v2 {
+ // See |async_bind_exception_port()| for details.
+ zx_status_t (*bind_exception_port)(async_dispatcher_t* dispatcher,
+ async_exception_t* exception);
+ // See |async_unbind_exception_port()| for details.
+ zx_status_t (*unbind_exception_port)(async_dispatcher_t* dispatcher,
+ async_exception_t* exception);
+ // See |async_resume_from_exception()| for details.
+ zx_status_t (*resume_from_exception)(async_dispatcher_t* dispatcher,
+ async_exception_t* exception,
+ zx_handle_t task,
+ uint32_t options);
+ } v2;
+} async_ops_t;
+
+struct async_dispatcher {
+ const async_ops_t* ops;
+};
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_DISPATCHER_H_
diff --git a/pkg/async/include/lib/async/exception.h b/pkg/async/include/lib/async/exception.h
new file mode 100644
index 0000000..654a294
--- /dev/null
+++ b/pkg/async/include/lib/async/exception.h
@@ -0,0 +1,84 @@
+// 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.
+
+#ifndef LIB_ASYNC_EXCEPTION_H_
+#define LIB_ASYNC_EXCEPTION_H_
+
+#include <lib/async/dispatcher.h>
+
+__BEGIN_CDECLS
+
+// Handles receipt of packets containing exception reports.
+//
+// The |status| is |ZX_OK| if the packet was successfully delivered and |data|
+// contains the information from the packet, otherwise |data| is null.
+// The |status| is |ZX_ERR_CANCELED| if the dispatcher was shut down.
+typedef void(async_exception_handler_t)(async_dispatcher_t* dispatcher,
+ async_exception_t* exception,
+ zx_status_t status,
+ const zx_port_packet_t* report);
+
+// Holds content for an exception packet receiver and its handler.
+//
+// The client is responsible for retaining the structure in memory
+// (and unmodified) until all packets have been received by the handler or the
+// dispatcher shuts down.
+//
+// Multiple packets may be delivered to the same receiver concurrently.
+struct async_exception {
+ // Private state owned by the dispatcher, initialize to zero with |ASYNC_STATE_INIT|.
+ async_state_t state;
+
+ // The handler to invoke when a packet is received.
+ async_exception_handler_t* handler;
+
+ // The task we're watching.
+ zx_handle_t task;
+
+ // The options to pass to zx_task_bind_exception_port().
+ uint32_t options;
+};
+
+// Bind the async port to the task's exception port.
+//
+// Returns |ZX_OK| if task's exception port was successfully bound to.
+// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+// Other error values are possible. See the documentation for
+// |zx_task_bind_exception_port()|.
+//
+// This operation is thread-safe.
+zx_status_t async_bind_exception_port(async_dispatcher_t* dispatcher,
+ async_exception_t* exception);
+
+// Unbind the async port from |task|'s exception port.
+//
+// Returns |ZX_OK| if the task's exception port was successfully unbound.
+// Returns |ZX_ERR_NOT_FOUND| if the port is not bound.
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+// Other error values are possible. See the documentation for
+// |zx_task_bind_exception_port()|.
+//
+// This operation is thread-safe.
+zx_status_t async_unbind_exception_port(async_dispatcher_t* dispatcher,
+ async_exception_t* exception);
+
+// Resume |task| after having processed an exception for it.
+// |options| is passed to |zx_task_resume_from_exception()|.
+//
+// Returns |ZX_OK| if the task was resumed.
+// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+// Other error values are possible. See the documentation for
+// |zx_task_resume_from_exception()|.
+//
+// This operation is thread-safe.
+zx_status_t async_resume_from_exception(async_dispatcher_t* dispatcher,
+ async_exception_t* exception,
+ zx_handle_t task,
+ uint32_t options);
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_EXCEPTION_H_
diff --git a/pkg/async/include/lib/async/receiver.h b/pkg/async/include/lib/async/receiver.h
new file mode 100644
index 0000000..453df71
--- /dev/null
+++ b/pkg/async/include/lib/async/receiver.h
@@ -0,0 +1,52 @@
+// Copyright 2017 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.
+
+#ifndef LIB_ASYNC_RECEIVER_H_
+#define LIB_ASYNC_RECEIVER_H_
+
+#include <lib/async/dispatcher.h>
+
+__BEGIN_CDECLS
+
+// Handles receipt of packets containing user supplied data.
+//
+// The |status| is |ZX_OK| if the packet was successfully delivered and |data|
+// contains the information from the packet, otherwise |data| is null.
+typedef void(async_receiver_handler_t)(async_dispatcher_t* dispatcher,
+ async_receiver_t* receiver,
+ zx_status_t status,
+ const zx_packet_user_t* data);
+
+// Holds content for a packet receiver and its handler.
+//
+// After successfully queuing packets to the receiver, the client is responsible
+// for retaining the structure in memory (and unmodified) until all packets have
+// been received by the handler or the dispatcher shuts down. There is no way
+// to cancel a packet which has been queued.
+//
+// Multiple packets may be delivered to the same receiver concurrently.
+struct async_receiver {
+ // Private state owned by the dispatcher, initialize to zero with |ASYNC_STATE_INIT|.
+ async_state_t state;
+
+ // The handler to invoke when a packet is received.
+ async_receiver_handler_t* handler;
+};
+
+// Enqueues a packet of data for delivery to a receiver.
+//
+// The |data| will be copied into the packet. May be NULL to create a
+// zero-initialized packet payload.
+//
+// Returns |ZX_OK| if the packet was successfully enqueued.
+// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+//
+// This operation is thread-safe.
+zx_status_t async_queue_packet(async_dispatcher_t* dispatcher, async_receiver_t* receiver,
+ const zx_packet_user_t* data);
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_RECEIVER_H_
\ No newline at end of file
diff --git a/pkg/async/include/lib/async/task.h b/pkg/async/include/lib/async/task.h
new file mode 100644
index 0000000..ee0b295
--- /dev/null
+++ b/pkg/async/include/lib/async/task.h
@@ -0,0 +1,70 @@
+// Copyright 2017 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.
+
+#ifndef LIB_ASYNC_TASK_H_
+#define LIB_ASYNC_TASK_H_
+
+#include <lib/async/dispatcher.h>
+
+__BEGIN_CDECLS
+
+// Handles execution of a posted task.
+//
+// The |status| is |ZX_OK| if the task's deadline elapsed and the task should run.
+// The |status| is |ZX_ERR_CANCELED| if the dispatcher was shut down before
+// the task's handler ran or the task was canceled.
+typedef void(async_task_handler_t)(async_dispatcher_t* dispatcher,
+ async_task_t* task,
+ zx_status_t status);
+
+// Holds context for a task and its handler.
+//
+// After successfully posting the task, the client is responsible for retaining
+// the structure in memory (and unmodified) until the task's handler runs, the task
+// is successfully canceled, or the dispatcher shuts down. Thereafter, the task
+// may be posted again or destroyed.
+struct async_task {
+ // Private state owned by the dispatcher, initialize to zero with |ASYNC_STATE_INIT|.
+ async_state_t state;
+
+ // The task's handler function.
+ async_task_handler_t* handler;
+
+ // The task's deadline must be expressed in the time base used by the asynchronous
+ // dispatcher (usually |ZX_CLOCK_MONOTONIC| except in unit tests).
+ // See |async_now()| for details.
+ zx_time_t deadline;
+};
+
+// Posts a task to run on or after its deadline following all posted
+// tasks with lesser or equal deadlines.
+//
+// The task's handler will be invoked exactly once unless the task is canceled.
+// When the dispatcher is shutting down (being destroyed), the handlers of
+// all remaining tasks will be invoked with a status of |ZX_ERR_CANCELED|.
+//
+// Returns |ZX_OK| if the task was successfully posted.
+// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+//
+// This operation is thread-safe.
+zx_status_t async_post_task(async_dispatcher_t* dispatcher, async_task_t* task);
+
+// Cancels the task associated with |task|.
+//
+// If successful, the task's handler will not run.
+//
+// Returns |ZX_OK| if the task was pending and it has been successfully
+// canceled; its handler will not run again and can be released immediately.
+// Returns |ZX_ERR_NOT_FOUND| if there was no pending task either because it
+// already ran, had not been posted, or has been dequeued and is pending
+// execution (perhaps on another thread).
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+//
+// This operation is thread-safe.
+zx_status_t async_cancel_task(async_dispatcher_t* dispatcher, async_task_t* task);
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_TASK_H_
\ No newline at end of file
diff --git a/pkg/async/include/lib/async/time.h b/pkg/async/include/lib/async/time.h
new file mode 100644
index 0000000..fe62cd5
--- /dev/null
+++ b/pkg/async/include/lib/async/time.h
@@ -0,0 +1,19 @@
+// 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.
+
+#ifndef LIB_ASYNC_TIME_H_
+#define LIB_ASYNC_TIME_H_
+
+#include <lib/async/dispatcher.h>
+
+__BEGIN_CDECLS
+
+// Returns the current time in the dispatcher's timebase.
+// For most loops, this is generally obtained from |ZX_CLOCK_MONOTONIC|
+// but certain loops may use a different tiembase, notably for testing.
+zx_time_t async_now(async_dispatcher_t* dispatcher);
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_TIME_H_
\ No newline at end of file
diff --git a/pkg/async/include/lib/async/trap.h b/pkg/async/include/lib/async/trap.h
new file mode 100644
index 0000000..814d9ef
--- /dev/null
+++ b/pkg/async/include/lib/async/trap.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef LIB_ASYNC_TRAP_H_
+#define LIB_ASYNC_TRAP_H_
+
+#include <lib/async/dispatcher.h>
+
+__BEGIN_CDECLS
+
+// Handles an asynchronous trap access.
+//
+// The |status| is |ZX_OK| if the bell was received and |bell| contains the
+// information from the packet, otherwise |bell| is null.
+typedef void(async_guest_bell_trap_handler_t)(async_dispatcher_t* dispatcher,
+ async_guest_bell_trap_t* trap,
+ zx_status_t status,
+ const zx_packet_guest_bell_t* bell);
+
+// Holds context for a bell trap and its handler.
+//
+// After successfully posting setting the trap, the client is responsible for retaining
+// the structure in memory (and unmodified) until the guest has been destroyed or the
+// dispatcher shuts down. There is no way to cancel a trap which has been set.
+struct async_guest_bell_trap {
+ // Private state owned by the dispatcher, initialize to zero with |ASYNC_STATE_INIT|.
+ async_state_t state;
+
+ // The handler to invoke to handle the trap access.
+ async_guest_bell_trap_handler_t* handler;
+};
+
+// Sets a bell trap in the guest to be handled asynchronously via a handler.
+//
+// |guest| is the handle of the guest the trap will be set on.
+// |addr| is the base address for the trap in the guest's physical address space.
+// |length| is the size of the trap in the guest's physical address space.
+//
+// Returns |ZX_OK| if the trap was successfully set.
+// Returns |ZX_ERR_ACCESS_DENIED| if the guest does not have |ZX_RIGHT_WRITE|.
+// Returns |ZX_ERR_ALREADY_EXISTS| if a bell trap with the same |addr| exists.
+// Returns |ZX_ERR_INVALID_ARGS| if |addr| or |length| are invalid.
+// Returns |ZX_ERR_OUT_OF_RANGE| if |addr| or |length| are out of range of the
+// address space.
+// Returns |ZX_ERR_WRONG_TYPE| if |guest| is not a handle to a guest.
+// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+//
+// This operation is thread-safe.
+zx_status_t async_set_guest_bell_trap(async_dispatcher_t* dispatcher, async_guest_bell_trap_t* trap,
+ zx_handle_t guest, zx_vaddr_t addr, size_t length);
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_TRAP_H_
\ No newline at end of file
diff --git a/pkg/async/include/lib/async/wait.h b/pkg/async/include/lib/async/wait.h
new file mode 100644
index 0000000..d359979
--- /dev/null
+++ b/pkg/async/include/lib/async/wait.h
@@ -0,0 +1,74 @@
+// Copyright 2017 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.
+
+#ifndef LIB_ASYNC_WAIT_H_
+#define LIB_ASYNC_WAIT_H_
+
+#include <lib/async/dispatcher.h>
+
+__BEGIN_CDECLS
+
+// Handles completion of asynchronous wait operations.
+//
+// The |status| is |ZX_OK| if the wait was satisfied and |signal| is non-null.
+// The |status| is |ZX_ERR_CANCELED| if the dispatcher was shut down before
+// the task's handler ran or the task was canceled.
+typedef void(async_wait_handler_t)(async_dispatcher_t* dispatcher,
+ async_wait_t* wait,
+ zx_status_t status,
+ const zx_packet_signal_t* signal);
+
+// Holds context for an asynchronous wait operation and its handler.
+//
+// After successfully beginning the wait, the client is responsible for retaining
+// the structure in memory (and unmodified) until the wait's handler runs, the wait
+// is successfully canceled, or the dispatcher shuts down. Thereafter, the wait
+// may be started begun or destroyed.
+struct async_wait {
+ // Private state owned by the dispatcher, initialize to zero with |ASYNC_STATE_INIT|.
+ async_state_t state;
+
+ // The wait's handler function.
+ async_wait_handler_t* handler;
+
+ // The object to wait for signals on.
+ zx_handle_t object;
+
+ // The set of signals to wait for.
+ zx_signals_t trigger;
+};
+
+// Begins asynchronously waiting for an object to receive one or more signals
+// specified in |wait|. Invokes the handler when the wait completes.
+//
+// The wait's handler will be invoked exactly once unless the wait is canceled.
+// When the dispatcher is shutting down (being destroyed), the handlers of
+// all remaining waits will be invoked with a status of |ZX_ERR_CANCELED|.
+//
+// Returns |ZX_OK| if the wait was successfully begun.
+// Returns |ZX_ERR_ACCESS_DENIED| if the object does not have |ZX_RIGHT_WAIT|.
+// Returns |ZX_ERR_BAD_STATE| if the dispatcher is shutting down.
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+//
+// This operation is thread-safe.
+zx_status_t async_begin_wait(async_dispatcher_t* dispatcher, async_wait_t* wait);
+
+// Cancels the wait associated with |wait|.
+//
+// If successful, the wait's handler will not run.
+//
+// Returns |ZX_OK| if the wait was pending and it has been successfully
+// canceled; its handler will not run again and can be released immediately.
+// Returns |ZX_ERR_NOT_FOUND| if there was no pending wait either because it
+// already completed, had not been started, or its completion packet has been
+// dequeued from the port and is pending delivery to its handler (perhaps on
+// another thread).
+// Returns |ZX_ERR_NOT_SUPPORTED| if not supported by the dispatcher.
+//
+// This operation is thread-safe.
+zx_status_t async_cancel_wait(async_dispatcher_t* dispatcher, async_wait_t* wait);
+
+__END_CDECLS
+
+#endif // LIB_ASYNC_WAIT_H_
\ No newline at end of file
diff --git a/pkg/async/meta.json b/pkg/async/meta.json
new file mode 100644
index 0000000..31913e9
--- /dev/null
+++ b/pkg/async/meta.json
@@ -0,0 +1,31 @@
+{
+ "banjo_deps": [],
+ "deps": [],
+ "fidl_deps": [],
+ "files": [
+ "pkg/async/ops.c",
+ "pkg/async/include/lib/async/dispatcher.h",
+ "pkg/async/include/lib/async/exception.h",
+ "pkg/async/include/lib/async/receiver.h",
+ "pkg/async/include/lib/async/task.h",
+ "pkg/async/include/lib/async/time.h",
+ "pkg/async/include/lib/async/trap.h",
+ "pkg/async/include/lib/async/wait.h"
+ ],
+ "headers": [
+ "pkg/async/include/lib/async/dispatcher.h",
+ "pkg/async/include/lib/async/exception.h",
+ "pkg/async/include/lib/async/receiver.h",
+ "pkg/async/include/lib/async/task.h",
+ "pkg/async/include/lib/async/time.h",
+ "pkg/async/include/lib/async/trap.h",
+ "pkg/async/include/lib/async/wait.h"
+ ],
+ "include_dir": "pkg/async/include",
+ "name": "async",
+ "root": "pkg/async",
+ "sources": [
+ "pkg/async/ops.c"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/async/ops.c b/pkg/async/ops.c
new file mode 100644
index 0000000..e3b1d6d
--- /dev/null
+++ b/pkg/async/ops.c
@@ -0,0 +1,62 @@
+// Copyright 2017 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/async/receiver.h>
+#include <lib/async/task.h>
+#include <lib/async/time.h>
+#include <lib/async/trap.h>
+#include <lib/async/wait.h>
+
+zx_time_t async_now(async_dispatcher_t* dispatcher) {
+ return dispatcher->ops->v1.now(dispatcher);
+}
+
+zx_status_t async_begin_wait(async_dispatcher_t* dispatcher, async_wait_t* wait) {
+ return dispatcher->ops->v1.begin_wait(dispatcher, wait);
+}
+
+zx_status_t async_cancel_wait(async_dispatcher_t* dispatcher, async_wait_t* wait) {
+ return dispatcher->ops->v1.cancel_wait(dispatcher, wait);
+}
+
+zx_status_t async_post_task(async_dispatcher_t* dispatcher, async_task_t* task) {
+ return dispatcher->ops->v1.post_task(dispatcher, task);
+}
+
+zx_status_t async_cancel_task(async_dispatcher_t* dispatcher, async_task_t* task) {
+ return dispatcher->ops->v1.cancel_task(dispatcher, task);
+}
+
+zx_status_t async_queue_packet(async_dispatcher_t* dispatcher, async_receiver_t* receiver,
+ const zx_packet_user_t* data) {
+ return dispatcher->ops->v1.queue_packet(dispatcher, receiver, data);
+}
+
+zx_status_t async_set_guest_bell_trap(async_dispatcher_t* dispatcher, async_guest_bell_trap_t* trap,
+ zx_handle_t guest, zx_vaddr_t addr, size_t length) {
+ return dispatcher->ops->v1.set_guest_bell_trap(dispatcher, trap, guest, addr, length);
+}
+
+zx_status_t async_bind_exception_port(async_dispatcher_t* dispatcher,
+ async_exception_t* exception) {
+ if (dispatcher->ops->version < ASYNC_OPS_V2)
+ return ZX_ERR_NOT_SUPPORTED;
+ return dispatcher->ops->v2.bind_exception_port(dispatcher, exception);
+}
+
+zx_status_t async_unbind_exception_port(async_dispatcher_t* dispatcher,
+ async_exception_t* exception) {
+ if (dispatcher->ops->version < ASYNC_OPS_V2)
+ return ZX_ERR_NOT_SUPPORTED;
+ return dispatcher->ops->v2.unbind_exception_port(dispatcher, exception);
+}
+
+zx_status_t async_resume_from_exception(async_dispatcher_t* dispatcher,
+ async_exception_t* exception,
+ zx_handle_t task,
+ uint32_t options) {
+ if (dispatcher->ops->version < ASYNC_OPS_V2)
+ return ZX_ERR_NOT_SUPPORTED;
+ return dispatcher->ops->v2.resume_from_exception(dispatcher, exception, task, options);
+}
diff --git a/pkg/fdio/include/lib/fdio/debug.h b/pkg/fdio/include/lib/fdio/debug.h
new file mode 100644
index 0000000..22acfb6
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/debug.h
@@ -0,0 +1,20 @@
+// Copyright 2016 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.
+
+#pragma once
+
+#include <stdio.h>
+#include <zircon/compiler.h>
+
+__BEGIN_CDECLS
+
+// per-file chatty debug macro
+#define xprintf(fmt, args...) \
+ do { \
+ if (ZXDEBUG) { \
+ printf("%s:%d: " fmt, __FILE__, __LINE__, ##args); \
+ } \
+ } while (0)
+
+__END_CDECLS
diff --git a/pkg/fdio/include/lib/fdio/io.h b/pkg/fdio/include/lib/fdio/io.h
new file mode 100644
index 0000000..b567cf4
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/io.h
@@ -0,0 +1,62 @@
+// Copyright 2016 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.
+
+#pragma once
+
+#include <limits.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <unistd.h> // for ssize_t
+
+#include <zircon/types.h>
+#include <zircon/compiler.h>
+
+#include <lib/fdio/limits.h>
+
+// flag on handle args in processargs
+// instructing that this fd should be dup'd to 0/1/2
+// and be used for all of stdio
+#define FDIO_FLAG_USE_FOR_STDIO 0x8000
+
+// events for fdio_wait_fd()
+#define FDIO_EVT_READABLE POLLIN
+#define FDIO_EVT_WRITABLE POLLOUT
+#define FDIO_EVT_ERROR POLLERR
+#define FDIO_EVT_PEER_CLOSED POLLRDHUP
+#define FDIO_EVT_ALL (POLLIN | POLLOUT | POLLERR | POLLRDHUP)
+
+__BEGIN_CDECLS
+
+// wait until one or more events are pending
+zx_status_t fdio_wait_fd(int fd, uint32_t events, uint32_t* pending, zx_time_t deadline);
+
+// create a fd that works with wait APIs (epoll, select, etc.) from a handle
+// and expected signals (signals_in/signals_out correspond to POLLIN/POLLOUT
+// events respectively). the handle will be closed when the fd is closed, unless
+// shared_handle is true.
+int fdio_handle_fd(zx_handle_t h, zx_signals_t signals_in, zx_signals_t signals_out, bool shared_handle);
+
+// invoke a raw fdio ioctl
+ssize_t fdio_ioctl(int fd, int op, const void* in_buf, size_t in_len, void* out_buf, size_t out_len);
+
+// create a pipe, installing one half in a fd, returning the other
+// for transport to another process
+zx_status_t fdio_pipe_half(zx_handle_t* handle, uint32_t* type);
+
+// Get a read-only VMO containing the whole contents of the file.
+// This function creates a clone of the underlying VMO when possible, falling
+// back to eagerly reading the contents into a freshly-created VMO.
+zx_status_t fdio_get_vmo_copy(int fd, zx_handle_t* out_vmo);
+
+// Gets a read-only VMO containing a clone of the underlying VMO.
+// This function will fail rather than copying the contents if it cannot clone.
+zx_status_t fdio_get_vmo_clone(int fd, zx_handle_t* out_vmo);
+
+// Get a read-only handle to the exact VMO used by the file system server to
+// represent the file. This function fails if the server does not have an exact
+// VMO representation of the file (e.g., if fdio_get_vmo would need to copy
+// or clone data into a new VMO).
+zx_status_t fdio_get_vmo_exact(int fd, zx_handle_t* out_vmo);
+
+__END_CDECLS
diff --git a/pkg/fdio/include/lib/fdio/limits.h b/pkg/fdio/include/lib/fdio/limits.h
new file mode 100644
index 0000000..d6c5b68
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/limits.h
@@ -0,0 +1,23 @@
+// Copyright 2017 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.
+
+#pragma once
+
+#include <limits.h>
+
+// Maximum number of fds per process.
+#define FDIO_MAX_FD 256
+
+// Maximum handles used in open/clone/create.
+#define FDIO_MAX_HANDLES 3
+
+// fdio_ops_t's read/write are able to do io of
+// at least this size.
+#define FDIO_CHUNK_SIZE 8192
+
+// Maximum size for an ioctl input.
+#define FDIO_IOCTL_MAX_INPUT 1024
+
+// Maximum length of a filename.
+#define FDIO_MAX_FILENAME NAME_MAX
diff --git a/pkg/fdio/include/lib/fdio/namespace.h b/pkg/fdio/include/lib/fdio/namespace.h
new file mode 100644
index 0000000..dcd7043
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/namespace.h
@@ -0,0 +1,95 @@
+// Copyright 2017 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.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS;
+
+typedef struct fdio_namespace fdio_ns_t;
+
+
+// Create a new, empty namespace
+zx_status_t fdio_ns_create(fdio_ns_t** out);
+
+// Destroy and deallocate a namespace
+// Will fail (ZX_ERR_BAD_STATE) if the namespace is in use.
+zx_status_t fdio_ns_destroy(fdio_ns_t* ns);
+
+// Create a new directory within a namespace, bound to the
+// directory-protocol-compatible handle h
+// The path must be an absolute path, like "/x/y/z", containing
+// no "." nor ".." entries. It is relative to the root of the
+// namespace.
+//
+// The handle is not closed on failure.
+//
+// Will fail with ZX_ERR_BAD_STATE if the namespace is in use.
+zx_status_t fdio_ns_bind(fdio_ns_t* ns, const char* path, zx_handle_t h);
+
+// Create a new directory within a namespace, bound to the
+// directory referenced by the file descriptor fd.
+// The path must be an absolute path, like "/x/y/z", containing
+// no "." nor ".." entries. It is relative to the root of the
+// namespace.
+//
+// The fd is not closed on success or failure.
+// Closing the fd after success does not affect namespace.
+//
+// Failures:
+// ZX_ERR_BAD_STATE: Namespace is already in use and immutable.
+// ZX_ERR_ALREADY_EXISTS: There is already a mounted directory there.
+// ZX_ERR_NOT_SUPPORTED: This path would shadow a mounted directory.
+zx_status_t fdio_ns_bind_fd(fdio_ns_t* ns, const char* path, int fd);
+
+// Open the root directory of the namespace as a file descriptor
+int fdio_ns_opendir(fdio_ns_t* ns);
+
+// chdir to / in the provided namespace
+zx_status_t fdio_ns_chdir(fdio_ns_t* ns);
+
+// Replace the fdio "global" namespace with the provided namespace
+zx_status_t fdio_ns_install(fdio_ns_t* ns);
+
+// Retrieve the fdio "global" namespace (if any).
+zx_status_t fdio_ns_get_installed(fdio_ns_t** ns);
+
+typedef struct fdio_flat_namespace {
+ size_t count;
+ zx_handle_t* handle;
+ uint32_t* type;
+ const char* const* path;
+} fdio_flat_namespace_t;
+
+// On success the caller takes ownership of a fdio_flat_namespace_t
+// containing a flat representation of the exported namespace (the
+// one provided in 'ns' or the active root namespace, respectively.)
+// The handles are CLONEs of the handles in the namespace and also
+// belong to the caller.
+// The whole data structure can be released with free(), keeping in
+// mind that the handles should be used or closed first.
+zx_status_t fdio_ns_export(fdio_ns_t* ns, fdio_flat_namespace_t** out);
+zx_status_t fdio_ns_export_root(fdio_flat_namespace_t** out);
+
+// Attempt to connect to a service through the namespace.
+// The handle is always consumed. It will be closed on error
+// or passed to the remote service on success.
+// The path must be an absolute path starting with / and containing
+// no ".." or "." or empty segments.
+zx_status_t fdio_ns_connect(fdio_ns_t* ns, const char* path,
+ uint32_t zxflags, zx_handle_t h);
+
+// Attempt a pipelined open through a namespace.
+// Success only indicates that the open was sent.
+// If the remote fails, the returned handle's peer will be closed.
+// The path must be an absolute path starting with / and containing
+// no ".." or "." or empty segments.
+zx_status_t fdio_ns_open(fdio_ns_t* ns, const char* path,
+ uint32_t zxflags, zx_handle_t* out);
+
+__END_CDECLS;
diff --git a/pkg/fdio/include/lib/fdio/private.h b/pkg/fdio/include/lib/fdio/private.h
new file mode 100644
index 0000000..2ceee14
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/private.h
@@ -0,0 +1,22 @@
+// Copyright 2017 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.
+
+#pragma once
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+#include <stdint.h>
+
+__BEGIN_CDECLS;
+
+// WARNING: These APIs are subject to change
+
+// __fdio_cleanpath cleans an input path, placing the output
+// in out, which is a buffer of at least "PATH_MAX" bytes.
+//
+// 'outlen' returns the length of the path placed in out, and 'is_dir'
+// is set to true if the returned path must be a directory.
+zx_status_t __fdio_cleanpath(const char* in, char* out, size_t* outlen, bool* is_dir);
+
+__END_CDECLS;
diff --git a/pkg/fdio/include/lib/fdio/spawn.h b/pkg/fdio/include/lib/fdio/spawn.h
new file mode 100644
index 0000000..94a656d
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/spawn.h
@@ -0,0 +1,217 @@
+// 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.
+
+#pragma once
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+__BEGIN_CDECLS
+
+// The |fdio_spawn| and |fdio_spawn_etc| functions allow some or all of the
+// environment of the running process to be shared with the process being
+// spawned.
+
+// Provides the spawned process with the job in which the process was created.
+//
+// The spawned process receives the job using the |PA_JOB_DEFAULT| process
+// argument.
+#define FDIO_SPAWN_CLONE_JOB ((uint32_t)0x0001u)
+
+// Provides the spawned process with the shared library loader resolved via
+// fuchsia.process.Resolver (if resolved), or that which is used by this
+// process.
+//
+// The shared library loader is passed as |PA_LDSVC_LOADER|.
+#define FDIO_SPAWN_DEFAULT_LDSVC ((uint32_t)0x0002u)
+// FDIO_SPAWN_CLONE_LDSVC is the same as FDIO_SPAWN_DEFAULT_LDSVC.
+// TODO(ZX-3031): this name is deprecated.
+#define FDIO_SPAWN_CLONE_LDSVC ((uint32_t)0x0002u)
+
+// Clones the filesystem namespace into the spawned process.
+#define FDIO_SPAWN_CLONE_NAMESPACE ((uint32_t)0x0004u)
+
+// Clones file descriptors 0, 1, and 2 into the spawned process.
+//
+// Skips any of these file descriptors that are closed without generating an
+// error.
+#define FDIO_SPAWN_CLONE_STDIO ((uint32_t)0x0008u)
+
+// Clones the environment into the spawned process.
+#define FDIO_SPAWN_CLONE_ENVIRON ((uint32_t)0x0010u)
+
+// Clones all of the above into the spawned process.
+#define FDIO_SPAWN_CLONE_ALL ((uint32_t)0xFFFFu)
+
+// Spawn a process in the given job.
+//
+// The program for the process is loaded from the given |path| and passed |argv|.
+// The aspects of this process' environment indicated by |flags| are shared with
+// the process. If the target program begins with |#!resolve | then the binary is
+// resolved by url via |fuchsia.process.Resolver|.
+//
+// The |argv| array must be terminated with a null pointer.
+//
+// If |job| is |ZX_HANDLE_INVALID|, then the process is spawned in
+// |zx_job_default()|. Does not take ownership of |job|.
+//
+// Upon success, |process_out| will be a handle to the process.
+zx_status_t fdio_spawn(zx_handle_t job,
+ uint32_t flags,
+ const char* path,
+ const char* const* argv,
+ zx_handle_t* process_out);
+
+// The |fdio_spawn_etc| function allows the running process to control the file
+// descriptor table in the process being spawned.
+
+// Duplicate a descriptor |local_fd| into |target_fd| in the spawned process.
+//
+// Uses the |fd| entry in the |fdio_spawn_action_t| union.
+#define FDIO_SPAWN_ACTION_CLONE_FD ((uint32_t)0x0001u)
+
+// Transfer local descriptor |local_fd| into |target_fd| in the spawned process.
+//
+// This action will fail if |local_fd| is not a valid |local_fd|, if |local_fd|
+// has been duplicated, if |local_fd| is being used in an io operation, or if
+// |local_fd| does not support this operation.
+//
+// From the point of view of the calling process, the |local_fd| will appear to
+// have been closed, regardless of whether the |fdio_spawn_etc| call succeeds.
+//
+// Uses the |fd| entry in the |fdio_spawn_action_t| union.
+#define FDIO_SPAWN_ACTION_TRANSFER_FD ((uint32_t)0x0002u)
+
+// Add the given entry to the namespace of the spawned process.
+//
+// If |FDIO_SPAWN_CLONE_NAMESPACE| is specified via |flags|, the namespace entry
+// is added to the cloned namespace from the calling process.
+//
+// The given handle will be closed regardless of whether the |fdio_spawn_etc|
+// call succeeds.
+//
+// Uses the |ns| entry in the |fdio_spawn_action_t| union.
+#define FDIO_SPAWN_ACTION_ADD_NS_ENTRY ((uint32_t)0x0003u)
+
+// Add the given handle to the process arguments of the spawned process.
+//
+// The given handle will be closed regardless of whether the |fdio_spawn_etc|
+// call succeeds.
+//
+// Uses the |h| entry in the |fdio_spawn_action_t| union.
+#define FDIO_SPAWN_ACTION_ADD_HANDLE ((uint32_t)0x0004u)
+
+// Sets the name of the spawned process to the given name.
+//
+// Overrides the default of use the first argument to name the process.
+//
+// Uses the |name| entry in the |fdio_spawn_action_t| union.
+#define FDIO_SPAWN_ACTION_SET_NAME ((uint32_t)0x0005u)
+
+// Instructs |fdio_spawn_etc| which actions to take.
+typedef struct fdio_spawn_action fdio_spawn_action_t;
+struct fdio_spawn_action {
+ // The action to take.
+ //
+ // See |FDIO_SPAWN_ACTION_*| above. If |action| is invalid, the action will
+ // be ignored (rather than generate an error).
+ uint32_t action;
+ union {
+ struct {
+ // The file descriptor in this process to clone or transfer.
+ int local_fd;
+
+ // The file descriptor in the spawned process that will receive the
+ // clone or transfer.
+ int target_fd;
+ } fd;
+ struct {
+ // The prefix in which to install the given handle in the namespace
+ // of the spawned process.
+ const char* prefix;
+
+ // The handle to install with the given prefix in the namespace of
+ // the spawned process.
+ zx_handle_t handle;
+ } ns;
+ struct {
+ // The process argument identifier of the handle to pass to the
+ // spawned process.
+ uint32_t id;
+
+ // The handle to pass to the process on startup.
+ zx_handle_t handle;
+ } h;
+ struct {
+ // The name to assign to the spawned process.
+ const char* data;
+ } name;
+ };
+};
+
+// The maximum size for error messages from |fdio_spawn_etc|.
+//
+// Including the null terminator.
+#define FDIO_SPAWN_ERR_MSG_MAX_LENGTH ((size_t)1024u)
+
+// Spawn a process in the given job.
+//
+// The binary for the process is loaded from the given |path| and passed |argv|.
+// The aspects of this process' environment indicated by |clone| are shared with
+// the process.
+//
+// The spawned process is also given |environ| as its environment and the given
+// |actions| are applied when creating the process.
+//
+// The |argv| array must be terminated with a null pointer.
+//
+// If non-null, the |environ| array must be terminated with a null pointer.
+//
+// If non-null, the |err_msg_out| buffer must have space for
+// |FDIO_SPAWN_ERR_MSG_MAX_LENGTH| bytes.
+//
+// If both |FDIO_SPAWN_CLONE_ENVIRON| and |environ| are specified, then the
+// spawned process is given |environ| as its environment. If both
+// |FDIO_SPAWN_CLONE_STDIO| and |actions| that target any of fds 0, 1, and 2 are
+// specified, then the actions that target those fds will control their
+// semantics in the spawned process.
+//
+// If |job| is |ZX_HANDLE_INVALID|, then the process is spawned in
+// |zx_job_default()|. Does not take ownership of |job|.
+//
+// Upon success, |process_out| will be a handle to the process. Upon failure, if
+// |err_msg_out| is not null, an error message will be we written to
+// |err_msg_out|, including a null terminator.
+zx_status_t fdio_spawn_etc(zx_handle_t job,
+ uint32_t flags,
+ const char* path,
+ const char* const* argv,
+ const char* const* environ,
+ size_t action_count,
+ const fdio_spawn_action_t* actions,
+ zx_handle_t* process_out,
+ char err_msg_out[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]);
+
+// Spawn a process using the given executable in the given job.
+//
+// See |fdio_spawn_etc| for details. Rather than loading the binary for the
+// process from a path, this function receives the binary as the contents of a
+// vmo.
+//
+// Always consumes |executable_vmo|.
+zx_status_t fdio_spawn_vmo(zx_handle_t job,
+ uint32_t flags,
+ zx_handle_t executable_vmo,
+ const char* const* argv,
+ const char* const* environ,
+ size_t action_count,
+ const fdio_spawn_action_t* actions,
+ zx_handle_t* process_out,
+ char err_msg_out[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]);
+
+__END_CDECLS
diff --git a/pkg/fdio/include/lib/fdio/unsafe.h b/pkg/fdio/include/lib/fdio/unsafe.h
new file mode 100644
index 0000000..c0f7e41
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/unsafe.h
@@ -0,0 +1,65 @@
+// Copyright 2017 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.
+
+#pragma once
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+#include <stdint.h>
+
+__BEGIN_CDECLS;
+
+// WARNING: These interfaces exist to allow integration of fdio file
+// descriptors with handle-centric message loops. If used incorrectly
+// they can seriously mess up the state of fdio, fds, etc.
+
+typedef struct fdio fdio_t;
+
+// This looks up a file descriptor, and if it exists,
+// upreferences the fdio_t under it and returns that.
+// fdio_unsafe_release() must be called later to release
+// the reference.
+//
+// If the fd does not exist, it returns NULL
+fdio_t* fdio_unsafe_fd_to_io(int fd);
+
+// Returns the handle corresponding to the underlying fdio,
+// if there is one. Returns ZX_HANDLE_INVALID otherwise.
+//
+// Since this handle is borrowed from the underlying fdio_t, it
+// is unsafe to close it or use it after fdio_unsafe_release is called.
+zx_handle_t fdio_unsafe_borrow_channel(fdio_t* io);
+
+// Releases a reference on a fdio_t. Used to "return"
+// a fdio_t obtained from fdio_unsafe_fd_to_io() when you're
+// done with it.
+void fdio_unsafe_release(fdio_t* io);
+
+// This given a fdio_t, and a bitmask of posix-style events
+// (EPOLLIN, EPOLLOUT, EPOLLERR), this returns a handle that
+// may be waited upon and a bitmask of which signals to
+// wait on for the desired events.
+//
+// The handle belongs to the fdio_t, is not duplicated,
+// and may be closed() by the fdio library but MUST NOT
+// be closed by the caller.
+//
+// If waiting is not supported by this fdio_t, the returned
+// handle is ZX_HANDLE_INVALID.
+//
+// This function is only safe to call on a fdio_t you
+// hold a reference to. It is not required that fdio_unsafe_wait_end() be
+// called after this.
+void fdio_unsafe_wait_begin(fdio_t* io, uint32_t events,
+ zx_handle_t* handle_out, zx_signals_t* signals_out);
+
+// This given a set of signals observed on a handle obtained
+// from fdio_unsafe_wait_begin() returns a set of posix-style events
+// that are indicated.
+//
+// This function is only safe to call on a fdio_t you
+// hold a reference to.
+void fdio_unsafe_wait_end(fdio_t* io, zx_signals_t signals, uint32_t* events_out);
+
+__END_CDECLS;
diff --git a/pkg/fdio/include/lib/fdio/util.h b/pkg/fdio/include/lib/fdio/util.h
new file mode 100644
index 0000000..fd30748
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/util.h
@@ -0,0 +1,115 @@
+// Copyright 2016 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.
+
+#pragma once
+
+#include <zircon/types.h>
+#include <zircon/compiler.h>
+#include <stdint.h>
+#include <unistd.h>
+
+__BEGIN_CDECLS
+
+// These routines are "internal" to fdio but used by some companion
+// code like userboot and devmgr
+
+typedef struct fdio fdio_t;
+
+// Utilities to help assemble handles for a new process
+// may return up to FDIO_MAX_HANDLES
+zx_status_t fdio_clone_cwd(zx_handle_t* handles, uint32_t* types);
+zx_status_t fdio_clone_fd(int fd, int newfd, zx_handle_t* handles, uint32_t* types);
+zx_status_t fdio_transfer_fd(int fd, int newfd, zx_handle_t* handles, uint32_t* types);
+
+// Attempt to create an fdio fd from some handles and their associated types,
+// as returned from fdio_transfer_fd.
+//
+// Can only create fds around:
+// - Remote IO objects
+// - Pipes
+// - Connected sockets
+//
+// This function transfers ownership of handles to the fd on success, and
+// closes them on failure.
+zx_status_t fdio_create_fd(zx_handle_t* handles, uint32_t* types, size_t hcount, int* fd_out);
+
+// attempt to install a fdio in the unistd fd table
+// if fd >= 0, request a specific fd, and starting_fd is ignored
+// if fd < 0, request the first available fd >= starting_fd
+// returns fd on success
+// the fdio must have been upref'd on behalf of the fdtab first
+int fdio_bind_to_fd(fdio_t* io, int fd, int starting_fd);
+
+// attempt to detach an fdio_t from the fd table
+// returns ZX_ERR_INVALID_ARGS if fd is out of range or doesn't exist
+// returns ZX_ERR_UNAVAILABLE if the fd is busy or has been dup'd
+// returns fdio_t via io_out with refcount 1 on success
+zx_status_t fdio_unbind_from_fd(int fd, fdio_t** io_out);
+
+// If this fd represents a "service" (an rpc channel speaking
+// an unknown fidl protocol or a fuchsia.io.* protocol),
+// this call will return ZX_OK and return the underlying handle.
+// On both success and failure, the fd is effectively closed.
+//
+// ZX_ERR_NOT_SUPPORTED is returned if this fd does not represent
+// a FIDL transport
+//
+// ZX_ERR_UNAVAILABLE is returned if this fd has been dup()'d and
+// duplicates are sharing the FIDL transport
+zx_status_t fdio_get_service_handle(int fd, zx_handle_t* out);
+
+// creates a do-nothing fdio_t
+fdio_t* fdio_null_create(void);
+
+// Wraps a channel with an fdio_t using remote io.
+// Takes ownership of h and e.
+fdio_t* fdio_remote_create(zx_handle_t h, zx_handle_t event);
+
+// creates a fdio that wraps a log object
+// this will allocate a per-thread buffer (on demand) to assemble
+// entire log-lines and flush them on newline or buffer full.
+fdio_t* fdio_logger_create(zx_handle_t);
+
+typedef struct zxio_storage zxio_storage_t;
+
+// Creates an |fdio_t| that is backed by a |zxio_t|.
+//
+// The |zxio_t| is initialized with a null ops table. The |zxio_storage_t| for
+// the |zxio_t| is returned via |out_storage|. The client can re-initialize the
+// |zxio_storage_t| to customize the behavior of the |zxio_t|.
+//
+// To bind the |fdio_t| to a file descriptor, use |fdio_bind_to_fd|.
+//
+// Upon failure, returns NULL.
+fdio_t* fdio_zxio_create(zxio_storage_t** out_storage);
+
+// Attempt to connect a channel to a named service.
+// On success the channel is connected. On failure
+// an error is returned and the handle is closed.
+zx_status_t fdio_service_connect(const char* svcpath, zx_handle_t h);
+
+// Attempt to connect a channel to a named service relative to dir.
+// On success the channel is connected. On failure
+// an error is returned and the handle is closed.
+zx_status_t fdio_service_connect_at(zx_handle_t dir, const char* path, zx_handle_t h);
+
+// Same as |fdio_service_connect|, but allows the passing of flags.
+zx_status_t fdio_open(const char* path, uint32_t zxflags, zx_handle_t h);
+
+// Same as |fdio_service_connect_at, but allows the passing of flags.
+zx_status_t fdio_open_at(zx_handle_t dir, const char* path, uint32_t zxflags, zx_handle_t h);
+
+// Attempt to clone a service handle by doing a pipelined
+// CLONE operation, returning the new channel endpoint,
+// or ZX_HANDLE_INVALID.
+zx_handle_t fdio_service_clone(zx_handle_t h);
+
+// Attempt to clone a service handle by doing a pipelined
+// CLONE operation, using the provided serving channel.
+// On success srv is bound to a clone of h. On failure
+// an error is returned and srv is closed.
+// Takes ownership of srv.
+zx_status_t fdio_service_clone_to(zx_handle_t h, zx_handle_t srv);
+
+__END_CDECLS
diff --git a/pkg/fdio/include/lib/fdio/vfs.h b/pkg/fdio/include/lib/fdio/vfs.h
new file mode 100644
index 0000000..f32ccb1
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/vfs.h
@@ -0,0 +1,82 @@
+// Copyright 2016 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.
+
+#pragma once
+
+#include <zircon/types.h>
+#include <zircon/listnode.h>
+#include <zircon/compiler.h>
+
+#include <stdio.h>
+#include <unistd.h> // ssize_t
+
+__BEGIN_CDECLS
+
+// On Fuchsia, the Block Device is transmitted by file descriptor, rather than
+// by path. This can prevent some racy behavior relating to FS start-up.
+#ifdef __Fuchsia__
+#define FS_FD_BLOCKDEVICE 200
+#endif
+
+// POSIX defines st_blocks to be the number of 512 byte blocks allocated
+// to the file. The "blkcnt" field of vnattr attempts to accomplish
+// this same goal, but by indirecting through VNATTR_BLKSIZE, we
+// reserve the right to change this "block size unit" (which is distinct from
+// "blksize", because POSIX) whenever we want.
+#define VNATTR_BLKSIZE 512
+
+typedef struct vnattr {
+ uint32_t valid; // mask of which bits to set for setattr
+ uint32_t mode;
+ uint64_t inode;
+ uint64_t size;
+ uint64_t blksize; // Block size for filesystem I/O
+ uint64_t blkcount; // Number of VNATTR_BLKSIZE byte blocks allocated
+ uint64_t nlink;
+ uint64_t create_time; // posix time (seconds since epoch)
+ uint64_t modify_time; // posix time
+} vnattr_t;
+
+// mask that identifies what fields to set in setattr
+#define ATTR_CTIME 0000001
+#define ATTR_MTIME 0000002
+#define ATTR_ATIME 0000004 // not yet implemented
+
+// bits compatible with POSIX stat
+#define V_TYPE_MASK 0170000
+#define V_TYPE_SOCK 0140000
+#define V_TYPE_LINK 0120000
+#define V_TYPE_FILE 0100000
+#define V_TYPE_BDEV 0060000
+#define V_TYPE_DIR 0040000
+#define V_TYPE_CDEV 0020000
+#define V_TYPE_PIPE 0010000
+
+#define V_ISUID 0004000
+#define V_ISGID 0002000
+#define V_ISVTX 0001000
+#define V_IRWXU 0000700
+#define V_IRUSR 0000400
+#define V_IWUSR 0000200
+#define V_IXUSR 0000100
+#define V_IRWXG 0000070
+#define V_IRGRP 0000040
+#define V_IWGRP 0000020
+#define V_IXGRP 0000010
+#define V_IRWXO 0000007
+#define V_IROTH 0000004
+#define V_IWOTH 0000002
+#define V_IXOTH 0000001
+
+#define VTYPE_TO_DTYPE(mode) (((mode)&V_TYPE_MASK) >> 12)
+#define DTYPE_TO_VTYPE(type) (((type)&15) << 12)
+
+typedef struct vdirent {
+ uint64_t ino;
+ uint8_t size;
+ uint8_t type;
+ char name[0];
+} __PACKED vdirent_t;
+
+__END_CDECLS
diff --git a/pkg/fdio/include/lib/fdio/watcher.h b/pkg/fdio/include/lib/fdio/watcher.h
new file mode 100644
index 0000000..441edcf
--- /dev/null
+++ b/pkg/fdio/include/lib/fdio/watcher.h
@@ -0,0 +1,43 @@
+// Copyright 2016 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.
+
+#pragma once
+
+#include <zircon/compiler.h>
+#include <lib/fdio/io.h>
+
+__BEGIN_CDECLS
+
+typedef zx_status_t (*watchdir_func_t)(int dirfd, int event, const char* fn, void* cookie);
+
+// This event occurs when a file is added or removed, including
+// (for fdio_watch_directory()) files that already exist.
+#define WATCH_EVENT_ADD_FILE 1
+#define WATCH_EVENT_REMOVE_FILE 2
+
+// This event occurs, once, when fdio_watch_directory() runs
+// out of existing files and has to start waiting for new
+// files to be added.
+#define WATCH_EVENT_IDLE 3
+
+// Call the provided callback (cb) for each file in directory
+// and each time a new file is added to the directory.
+//
+// If the callback returns a status other than ZX_OK, watching
+// stops and the callback's status is returned to the caller
+// of fdio_watch_directory.
+//
+// If the deadline expires, ZX_ERR_TIMED_OUT is returned to the
+// caller. A deadline of ZX_TIME_INFINITE will never expire.
+//
+// The callback may use ZX_ERR_STOP as a way to signal to the
+// caller that it wants to stop because it found what it was
+// looking for, etc -- since this error code is not returned
+// by syscalls or public APIs, the callback does not need to
+// worry about it turning up normally.
+
+zx_status_t fdio_watch_directory(int dirfd, watchdir_func_t cb, zx_time_t deadline, void* cookie);
+
+
+__END_CDECLS
diff --git a/pkg/fdio/meta.json b/pkg/fdio/meta.json
new file mode 100644
index 0000000..51babcb
--- /dev/null
+++ b/pkg/fdio/meta.json
@@ -0,0 +1,32 @@
+{
+ "binaries": {
+ "arm64": {
+ "debug": ".build-id/eb/9ed8b78bc5865718b59bd2a3a47e3e28650f8f.debug",
+ "dist": "arch/arm64/dist/libfdio.so",
+ "link": "arch/arm64/lib/libfdio.so"
+ },
+ "x64": {
+ "debug": ".build-id/fb/6715ba574ce88bcfca5045ee90fef94163808f.debug",
+ "dist": "arch/x64/dist/libfdio.so",
+ "link": "arch/x64/lib/libfdio.so"
+ }
+ },
+ "deps": [],
+ "format": "shared",
+ "headers": [
+ "pkg/fdio/include/lib/fdio/debug.h",
+ "pkg/fdio/include/lib/fdio/io.h",
+ "pkg/fdio/include/lib/fdio/limits.h",
+ "pkg/fdio/include/lib/fdio/namespace.h",
+ "pkg/fdio/include/lib/fdio/private.h",
+ "pkg/fdio/include/lib/fdio/spawn.h",
+ "pkg/fdio/include/lib/fdio/unsafe.h",
+ "pkg/fdio/include/lib/fdio/util.h",
+ "pkg/fdio/include/lib/fdio/vfs.h",
+ "pkg/fdio/include/lib/fdio/watcher.h"
+ ],
+ "include_dir": "pkg/fdio/include",
+ "name": "fdio",
+ "root": "pkg/fdio",
+ "type": "cc_prebuilt_library"
+}
\ No newline at end of file
diff --git a/pkg/fidl-async/bind.c b/pkg/fidl-async/bind.c
new file mode 100644
index 0000000..fad7132
--- /dev/null
+++ b/pkg/fidl-async/bind.c
@@ -0,0 +1,147 @@
+// 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/async/wait.h>
+#include <lib/fidl-async/bind.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zircon/syscalls.h>
+
+typedef struct fidl_binding {
+ async_wait_t wait;
+ fidl_dispatch_t* dispatch;
+ async_dispatcher_t* dispatcher;
+ void* ctx;
+ const void* ops;
+} fidl_binding_t;
+
+typedef struct fidl_connection {
+ fidl_txn_t txn;
+ zx_handle_t channel;
+ zx_txid_t txid;
+ fidl_binding_t* binding;
+} fidl_connection_t;
+
+static zx_status_t fidl_reply(fidl_txn_t* txn, const fidl_msg_t* msg) {
+ fidl_connection_t* conn = (fidl_connection_t*)txn;
+ if (conn->txid == 0u)
+ return ZX_ERR_BAD_STATE;
+ if (msg->num_bytes < sizeof(fidl_message_header_t))
+ return ZX_ERR_INVALID_ARGS;
+ fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;
+ hdr->txid = conn->txid;
+ conn->txid = 0u;
+ return zx_channel_write(conn->channel, 0, msg->bytes, msg->num_bytes,
+ msg->handles, msg->num_handles);
+}
+
+static void fidl_binding_destroy(fidl_binding_t* binding) {
+ zx_handle_close(binding->wait.object);
+ free(binding);
+}
+
+static void fidl_message_handler(async_dispatcher_t* dispatcher,
+ async_wait_t* wait,
+ zx_status_t status,
+ const zx_packet_signal_t* signal) {
+ fidl_binding_t* binding = (fidl_binding_t*)wait;
+ if (status != ZX_OK) {
+ goto shutdown;
+ }
+
+ if (signal->observed & ZX_CHANNEL_READABLE) {
+ char bytes[ZX_CHANNEL_MAX_MSG_BYTES];
+ zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
+ for (uint64_t i = 0; i < signal->count; i++) {
+ fidl_msg_t msg = {
+ .bytes = bytes,
+ .handles = handles,
+ .num_bytes = 0u,
+ .num_handles = 0u,
+ };
+ status = zx_channel_read(wait->object, 0, bytes, handles,
+ ZX_CHANNEL_MAX_MSG_BYTES,
+ ZX_CHANNEL_MAX_MSG_HANDLES,
+ &msg.num_bytes, &msg.num_handles);
+ if (status == ZX_ERR_SHOULD_WAIT) {
+ break;
+ }
+ if (status != ZX_OK || msg.num_bytes < sizeof(fidl_message_header_t)) {
+ goto shutdown;
+ }
+ fidl_message_header_t* hdr = (fidl_message_header_t*)msg.bytes;
+ fidl_connection_t conn = {
+ .txn.reply = fidl_reply,
+ .channel = wait->object,
+ .txid = hdr->txid,
+ .binding = binding,
+ };
+ status = binding->dispatch(binding->ctx, &conn.txn, &msg, binding->ops);
+ switch (status) {
+ case ZX_OK:
+ status = async_begin_wait(dispatcher, wait);
+ if (status != ZX_OK) {
+ goto shutdown;
+ }
+ return;
+ case ZX_ERR_ASYNC:
+ return;
+ default:
+ goto shutdown;
+ }
+ }
+ }
+
+shutdown:
+ fidl_binding_destroy(binding);
+}
+
+zx_status_t fidl_bind(async_dispatcher_t* dispatcher, zx_handle_t channel,
+ fidl_dispatch_t* dispatch, void* ctx, const void* ops) {
+ fidl_binding_t* binding = calloc(1, sizeof(fidl_binding_t));
+ binding->wait.handler = fidl_message_handler;
+ binding->wait.object = channel;
+ binding->wait.trigger = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
+ binding->dispatch = dispatch;
+ binding->dispatcher = dispatcher;
+ binding->ctx = ctx;
+ binding->ops = ops;
+ zx_status_t status = async_begin_wait(dispatcher, &binding->wait);
+ if (status != ZX_OK) {
+ fidl_binding_destroy(binding);
+ }
+ return status;
+}
+
+typedef struct fidl_async_txn {
+ fidl_connection_t connection;
+} fidl_async_txn_t;
+
+fidl_async_txn_t* fidl_async_txn_create(fidl_txn_t* txn) {
+ fidl_connection_t* connection = (fidl_connection_t*) txn;
+
+ fidl_async_txn_t* async_txn = calloc(1, sizeof(fidl_async_txn_t));
+ memcpy(&async_txn->connection, connection, sizeof(*connection));
+
+ return async_txn;
+}
+
+fidl_txn_t* fidl_async_txn_borrow(fidl_async_txn_t* async_txn) {
+ return &async_txn->connection.txn;
+}
+
+zx_status_t fidl_async_txn_complete(fidl_async_txn_t* async_txn, bool rebind) {
+ zx_status_t status = ZX_OK;
+ if (rebind) {
+ status = async_begin_wait(async_txn->connection.binding->dispatcher,
+ &async_txn->connection.binding->wait);
+ if (status == ZX_OK) {
+ return ZX_OK;
+ }
+ }
+
+ fidl_binding_destroy(async_txn->connection.binding);
+ free(async_txn);
+ return status;
+}
diff --git a/pkg/fidl-async/include/lib/fidl-async/bind.h b/pkg/fidl-async/include/lib/fidl-async/bind.h
new file mode 100644
index 0000000..5fc6f8b
--- /dev/null
+++ b/pkg/fidl-async/include/lib/fidl-async/bind.h
@@ -0,0 +1,92 @@
+// 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.
+
+#ifndef LIB_FIDL_BIND_H_
+#define LIB_FIDL_BIND_H_
+
+#include <lib/async/dispatcher.h>
+#include <zircon/fidl.h>
+
+__BEGIN_CDECLS
+
+// A generic FIDL dispatch function.
+//
+// For FIDL interfaces with [Layout="Simple"], the C backend generates a
+// dispatch function that decodes the |msg| and calls through an |ops| table.
+//
+// This function signature matches the structure of these generated functions
+// but with the type of the |ops| table erased.
+//
+// Example:
+//
+// fidl_bind(dispacher, channel, (fidl_dispatch_t*)spaceship_SpaceShip_dispatch, ctx, &kOps);
+//
+typedef zx_status_t(fidl_dispatch_t)(void* ctx, fidl_txn_t* txn,
+ fidl_msg_t* msg, const void* ops);
+
+// Binds a |dispatch| function to channel| using |dispatcher|.
+//
+// This function adds an |async_wait_t| to the given |dispatcher| that waits
+// asynchronously for new messages to arrive on |channel|. When a message
+// arrives, the |dispatch| function is called on one of the threads associated
+// with the |dispatcher| with the |fidl_msg_t| as well as the given |ctx| and
+// |ops|.
+//
+// Typically, the |dispatch| function is generated by the C backend for FIDL
+// interfaces with with [Layout="Simple"] (see |fidl_dispatch_t|). These
+// dispatch functions decode the |fidl_msg_t| and call through the |ops| table
+// implementations of the interface's methods, passing along the |ctx| and a
+// |fidl_txn_t| (if the method has a reply message).
+//
+// The |fidl_txn_t| passed to |dispatch| is valid only until |dispatch| returns.
+// If the method has a reply message, the |reply| function on the |fidl_txn_t|
+// object must be called synchronously within the |dispatch| call.
+//
+// If a client wishes to reply to the message asynchronously, |fidl_async_txn_create|
+// must be invoked on |fidl_txn_t|, and ZX_ERR_ASYNC must be returned.
+//
+// Returns whether |fidl_bind| was able to begin waiting on the given |channel|.
+// Upon any error, |channel| is closed and the binding is terminated. Shutting down
+// the |dispatcher| also results in |channel| being closed.
+//
+// It is safe to shutdown the dispatcher at any time.
+//
+// It is unsafe to destroy the dispatcher from within a dispatch function.
+// It is unsafe to destroy the dispatcher while any |fidl_async_txn_t| objects
+// are alive.
+zx_status_t fidl_bind(async_dispatcher_t* dispatcher, zx_handle_t channel,
+ fidl_dispatch_t* dispatch, void* ctx, const void* ops);
+
+// An asynchronous FIDL txn.
+//
+// This is an opaque wrapper around |fidl_txn_t| which can extend the lifetime
+// of the object beyond the dispatched function.
+typedef struct fidl_async_txn fidl_async_txn_t;
+
+// Takes ownership of |txn| and allows usage of the txn beyond the currently
+// dispatched function.
+//
+// If this function is invoked within a dispatched function, that function
+// must return ZX_ERR_ASYNC.
+//
+// The result must be destroyed with a call to |fidl_async_txn_complete|.
+fidl_async_txn_t* fidl_async_txn_create(fidl_txn_t* txn);
+
+// Acquire a reference to the |fidl_txn_t| backing this txn object.
+//
+// It is unsafe to use this |fidl_txn_t| after |async_txn| is completed.
+fidl_txn_t* fidl_async_txn_borrow(fidl_async_txn_t* async_txn);
+
+// Destroys an asynchronous transaction created with |fidl_async_txn_create|.
+//
+// If requested, rebinds the underlying txn against the binding.
+// Returns an error if |rebind| is true and the transaction could not be
+// re-bound.
+//
+// In all cases, the |async_txn| object is consumed.
+zx_status_t fidl_async_txn_complete(fidl_async_txn_t* async_txn, bool rebind);
+
+__END_CDECLS
+
+#endif // LIB_FIDL_BIND_H_
diff --git a/pkg/fidl-async/meta.json b/pkg/fidl-async/meta.json
new file mode 100644
index 0000000..f5dfdbb
--- /dev/null
+++ b/pkg/fidl-async/meta.json
@@ -0,0 +1,22 @@
+{
+ "banjo_deps": [],
+ "deps": [
+ "async",
+ "fidl"
+ ],
+ "fidl_deps": [],
+ "files": [
+ "pkg/fidl-async/bind.c",
+ "pkg/fidl-async/include/lib/fidl-async/bind.h"
+ ],
+ "headers": [
+ "pkg/fidl-async/include/lib/fidl-async/bind.h"
+ ],
+ "include_dir": "pkg/fidl-async/include",
+ "name": "fidl-async",
+ "root": "pkg/fidl-async",
+ "sources": [
+ "pkg/fidl-async/bind.c"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/fidl/buffer_walker.h b/pkg/fidl/buffer_walker.h
new file mode 100644
index 0000000..92cb2e1
--- /dev/null
+++ b/pkg/fidl/buffer_walker.h
@@ -0,0 +1,809 @@
+// Copyright 2017 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.
+
+#pragma once
+
+namespace fidl {
+namespace internal {
+
+// Some assumptions about data type layout.
+static_assert(offsetof(fidl_string_t, size) == 0u, "");
+static_assert(offsetof(fidl_string_t, data) == 8u, "");
+
+static_assert(offsetof(fidl_vector_t, count) == 0u, "");
+static_assert(offsetof(fidl_vector_t, data) == 8u, "");
+
+static_assert(ZX_HANDLE_INVALID == FIDL_HANDLE_ABSENT, "");
+
+template <bool kConst, class U>
+struct SetPtrConst;
+
+template <class U>
+struct SetPtrConst<false, U> {
+ typedef U* type;
+};
+
+template <class U>
+struct SetPtrConst<true, U> {
+ typedef const U* type;
+};
+
+// Walks over a FIDL buffer and validates/encodes/decodes it per-Derived.
+//
+// kMutating controls whether this deals with mutable bytes or immutable bytes
+// (validation wants immutable, encode/decode wants mutable)
+//
+// kContinueAfterErrors controls whether parsing is continued upon failure (encode needs this to
+// see all available handles).
+//
+// Derived should offer the following methods:
+//
+// const? uint8_t* bytes() - returns the start of the buffer of bytes
+// uint32_t num_bytes() - returns the number of bytes in said buffer
+// uint32_t num_handles() - returns the number of handles that are claimable
+// bool ValidateOutOfLineStorageClaim(const void* a, const void* b)
+// - returns true if a legally points to b
+// void UnclaimedHandle(zx_handle_t*) - notes that a handle was skipped
+// void ClaimedHandle(zx_handle_t*, uint32_t idx) - notes that a handle was claimed
+// PointerState GetPointerState(const void* ptr) - returns whether a pointer is present or not
+// HandleState GetHandleState(zx_handle_t) - returns if a handle is present or not
+// void UpdatePointer(T**p, T*v) - mutates a pointer representation for a present pointer
+// void SetError(const char* error_msg) - flags that an error occurred
+template <class Derived, bool kMutating, bool kContinueAfterErrors>
+class BufferWalker {
+public:
+ explicit BufferWalker(const fidl_type* type)
+ : type_(type) {}
+
+ void Walk() {
+ // The first decode is special. It must be a struct or a table.
+ // We need to know the size of the first element to compute the start of
+ // the out-of-line allocations.
+
+ if (type_ == nullptr) {
+ SetError("Cannot decode a null fidl type");
+ return;
+ }
+
+ if (bytes() == nullptr) {
+ SetError("Cannot decode null bytes");
+ return;
+ }
+
+ switch (type_->type_tag) {
+ case fidl::kFidlTypeStruct:
+ if (num_bytes() < type_->coded_struct.size) {
+ SetError("Message size is smaller than expected");
+ return;
+ }
+ out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(type_->coded_struct.size));
+ break;
+ case fidl::kFidlTypeTable:
+ if (num_bytes() < sizeof(fidl_vector_t)) {
+ SetError("Message size is smaller than expected");
+ return;
+ }
+ out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(sizeof(fidl_vector_t)));
+ break;
+ default:
+ SetError("Message must be a struct or a table");
+ return;
+ }
+
+ Push(Frame::DoneSentinel());
+ Push(Frame(type_, 0u));
+
+// Macro to insert the relevant goop required to support two control flows here:
+// one where we keep reading after error, and another where we return immediately.
+// No runtime overhead thanks to if constexpr magic.
+#define FIDL_POP_AND_CONTINUE_OR_RETURN \
+ if (kContinueAfterErrors) { \
+ Pop(); \
+ continue; \
+ } else { \
+ return; \
+ }
+
+ for (;;) {
+ Frame* frame = Peek();
+
+ switch (frame->state) {
+ case Frame::kStateStruct: {
+ const uint32_t field_index = frame->NextStructField();
+ if (field_index == frame->struct_state.field_count) {
+ Pop();
+ continue;
+ }
+ const fidl::FidlField& field = frame->struct_state.fields[field_index];
+ const fidl_type_t* field_type = field.type;
+ const uint32_t field_offset = frame->offset + field.offset;
+ if (!Push(Frame(field_type, field_offset))) {
+ SetError("recursion depth exceeded processing struct");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ continue;
+ }
+ case Frame::kStateStructPointer: {
+ switch (GetPointerState(TypedAt<void>(frame->offset))) {
+ case PointerState::PRESENT:
+ break;
+ case PointerState::ABSENT:
+ Pop();
+ continue;
+ default:
+ SetError("Tried to decode a bad struct pointer");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ auto struct_ptr_ptr = TypedAt<void*>(frame->offset);
+ if (!ClaimOutOfLineStorage(frame->struct_pointer_state.struct_type->size,
+ *struct_ptr_ptr, &frame->offset)) {
+ SetError("message wanted to store too large of a nullable struct");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ UpdatePointer(struct_ptr_ptr, TypedAt<void>(frame->offset));
+ const fidl::FidlCodedStruct* coded_struct = frame->struct_pointer_state.struct_type;
+ *frame = Frame(coded_struct, frame->offset);
+ continue;
+ }
+ case Frame::kStateTable: {
+ if (frame->field == 0u) {
+ auto envelope_vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
+ switch (GetPointerState(&envelope_vector_ptr->data)) {
+ case PointerState::PRESENT:
+ break;
+ case PointerState::ABSENT:
+ SetError("Table data cannot be absent");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ default:
+ SetError("message tried to decode a non-present vector");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ uint32_t size;
+ if (mul_overflow(envelope_vector_ptr->count, 2 * sizeof(uint64_t), &size)) {
+ SetError("integer overflow calculating table size");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (!ClaimOutOfLineStorage(size, envelope_vector_ptr->data, &frame->offset)) {
+ SetError("message wanted to store too large of a table");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ UpdatePointer(&envelope_vector_ptr->data, TypedAt<void>(frame->offset));
+ frame->field = 1;
+ frame->table_state.known_index = 0;
+ frame->table_state.present_count = static_cast<uint32_t>(envelope_vector_ptr->count);
+ frame->table_state.end_offset = out_of_line_offset_;
+ frame->table_state.end_handle = handle_idx_;
+ continue;
+ }
+ if (frame->table_state.end_offset != out_of_line_offset_) {
+ SetError("Table field was mis-sized");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (frame->table_state.end_handle != handle_idx_) {
+ SetError("Table handles were mis-sized");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (frame->field > frame->table_state.present_count) {
+ Pop();
+ continue;
+ }
+ const fidl::FidlTableField* known_field = nullptr;
+ if (frame->table_state.known_index < frame->table_state.field_count) {
+ const fidl::FidlTableField* field =
+ &frame->table_state.fields[frame->table_state.known_index];
+ if (field->ordinal == frame->field) {
+ known_field = field;
+ frame->table_state.known_index++;
+ }
+ }
+ const uint32_t tag_offset = static_cast<uint32_t>(
+ frame->offset + (frame->field - 1) * 2 * sizeof(uint64_t));
+ const uint32_t data_offset = static_cast<uint32_t>(
+ tag_offset + sizeof(uint64_t));
+ const uint64_t packed_sizes = *TypedAt<uint64_t>(tag_offset);
+ frame->field++;
+ switch (GetPointerState(TypedAt<void>(data_offset))) {
+ case PointerState::PRESENT:
+ if (packed_sizes != 0)
+ break; // expected
+
+ SetError("Table envelope has present data pointer, but no data, and no handles");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ case PointerState::ABSENT:
+ if (packed_sizes == 0)
+ continue; // skip
+
+ SetError("Table envelope has absent data pointer, yet has data and/or handles");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ default:
+ SetError("Table envelope has bad data pointer");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ uint32_t offset;
+ uint32_t handles;
+ const uint32_t table_bytes = static_cast<uint32_t>(packed_sizes & 0xffffffffu);
+ const uint32_t table_handles = static_cast<uint32_t>(packed_sizes >> 32);
+ if (add_overflow(out_of_line_offset_, table_bytes, &offset) || offset > num_bytes()) {
+ SetError("integer overflow decoding table field");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (add_overflow(handle_idx_, table_handles, &handles) ||
+ handles > num_handles()) {
+ SetError("integer overflow decoding table handles");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ frame->table_state.end_offset = offset;
+ frame->table_state.end_handle = handles;
+ if (known_field != nullptr) {
+ const fidl_type_t* field_type = known_field->type;
+ uint32_t field_offset;
+ if (!ClaimOutOfLineStorage(TypeSize(field_type), TypedAt<void*>(data_offset), &field_offset)) {
+ SetError("table wanted too many bytes in field");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset));
+ if (!Push(Frame(field_type, field_offset))) {
+ SetError("recursion depth exceeded decoding table");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ } else {
+ // Table data will not be processed: discard it.
+ uint32_t field_offset;
+ if (!ClaimOutOfLineStorage(table_bytes, TypedAt<void*>(data_offset), &field_offset)) {
+ SetError("table wanted too many bytes in field");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset));
+ for (uint32_t i = 0; i < table_handles; i++) {
+ if (!ClaimHandle(nullptr)) {
+ SetError("expected handle not present");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ }
+ }
+ continue;
+ }
+ case Frame::kStateTablePointer: {
+ switch (GetPointerState(TypedAt<void>(frame->offset))) {
+ case PointerState::PRESENT:
+ break;
+ case PointerState::ABSENT:
+ Pop();
+ continue;
+ default:
+ SetError("Tried to decode a bad table pointer");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ auto table_ptr_ptr = TypedAt<void*>(frame->offset);
+ if (!ClaimOutOfLineStorage(sizeof(fidl_vector_t), *table_ptr_ptr, &frame->offset)) {
+ SetError("message wanted to store too large of a nullable table");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ UpdatePointer(table_ptr_ptr, TypedAt<void>(frame->offset));
+ const fidl::FidlCodedTable* coded_table = frame->table_pointer_state.table_type;
+ *frame = Frame(coded_table, frame->offset);
+ continue;
+ }
+ case Frame::kStateUnion: {
+ fidl_union_tag_t union_tag = *TypedAt<fidl_union_tag_t>(frame->offset);
+ if (union_tag >= frame->union_state.type_count) {
+ SetError("Tried to decode a bad union discriminant");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ const fidl_type_t* member = frame->union_state.types[union_tag];
+ if (!member) {
+ Pop();
+ continue;
+ }
+ frame->offset += frame->union_state.data_offset;
+ *frame = Frame(member, frame->offset);
+ continue;
+ }
+ case Frame::kStateUnionPointer: {
+ auto union_ptr_ptr = TypedAt<fidl_union_tag_t*>(frame->offset);
+ switch (GetPointerState(TypedAt<void>(frame->offset))) {
+ case PointerState::PRESENT:
+ break;
+ case PointerState::ABSENT:
+ Pop();
+ continue;
+ default:
+ SetError("Tried to decode a bad union pointer");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (!ClaimOutOfLineStorage(frame->union_pointer_state.union_type->size, *union_ptr_ptr,
+ &frame->offset)) {
+ SetError("message wanted to store too large of a nullable union");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ UpdatePointer(union_ptr_ptr, TypedAt<fidl_union_tag_t>(frame->offset));
+ const fidl::FidlCodedUnion* coded_union = frame->union_pointer_state.union_type;
+ *frame = Frame(coded_union, frame->offset);
+ continue;
+ }
+ case Frame::kStateArray: {
+ const uint32_t element_offset = frame->NextArrayOffset();
+ if (element_offset == frame->array_state.array_size) {
+ Pop();
+ continue;
+ }
+ const fidl_type_t* element_type = frame->array_state.element;
+ const uint32_t offset = frame->offset + element_offset;
+ if (!Push(Frame(element_type, offset))) {
+ SetError("recursion depth exceeded decoding array");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ continue;
+ }
+ case Frame::kStateString: {
+ auto string_ptr = TypedAt<fidl_string_t>(frame->offset);
+ // The string storage may be Absent for nullable strings and must
+ // otherwise be Present. No other values are allowed.
+ switch (GetPointerState(&string_ptr->data)) {
+ case PointerState::PRESENT:
+ break;
+ case PointerState::ABSENT:
+ if (!frame->string_state.nullable) {
+ SetError("message tried to decode an absent non-nullable string");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (string_ptr->size != 0u) {
+ SetError("message tried to decode an absent string of non-zero length");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ Pop();
+ continue;
+ default:
+ SetError(
+ "message tried to decode a string that is neither present nor absent");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ uint64_t bound = frame->string_state.max_size;
+ uint64_t size = string_ptr->size;
+ if (size > bound) {
+ SetError("message tried to decode too large of a bounded string");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ uint32_t string_data_offset = 0u;
+ if (!ClaimOutOfLineStorage(static_cast<uint32_t>(size), string_ptr->data, &string_data_offset)) {
+ SetError("decoding a string overflowed buffer");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ UpdatePointer(&string_ptr->data, TypedAt<char>(string_data_offset));
+ Pop();
+ continue;
+ }
+ case Frame::kStateHandle: {
+ auto handle_ptr = TypedAt<zx_handle_t>(frame->offset);
+ // The handle storage may be Absent for nullable handles and must
+ // otherwise be Present. No other values are allowed.
+ switch (GetHandleState(*handle_ptr)) {
+ case HandleState::ABSENT:
+ if (frame->handle_state.nullable) {
+ Pop();
+ continue;
+ }
+ SetError("message tried to decode a non-present handle");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ case HandleState::PRESENT:
+ if (!ClaimHandle(handle_ptr)) {
+ SetError("message decoded too many handles");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ Pop();
+ continue;
+ default:
+ // The value at the handle was garbage.
+ SetError("message tried to decode a garbage handle");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ }
+ case Frame::kStateVector: {
+ auto vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
+ // The vector storage may be Absent for nullable vectors and must
+ // otherwise be Present. No other values are allowed.
+ switch (GetPointerState(&vector_ptr->data)) {
+ case PointerState::PRESENT:
+ break;
+ case PointerState::ABSENT:
+ if (!frame->vector_state.nullable) {
+ SetError("message tried to decode an absent non-nullable vector");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (vector_ptr->count != 0u) {
+ SetError("message tried to decode an absent vector of non-zero elements");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ Pop();
+ continue;
+ default:
+ SetError("message tried to decode a non-present vector");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (vector_ptr->count > frame->vector_state.max_count) {
+ SetError("message tried to decode too large of a bounded vector");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ uint32_t size;
+ if (mul_overflow(vector_ptr->count, frame->vector_state.element_size, &size)) {
+ SetError("integer overflow calculating vector size");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ if (!ClaimOutOfLineStorage(size, vector_ptr->data, &frame->offset)) {
+ SetError("message wanted to store too large of a vector");
+ FIDL_POP_AND_CONTINUE_OR_RETURN;
+ }
+ UpdatePointer(&vector_ptr->data, TypedAt<void>(frame->offset));
+ if (frame->vector_state.element) {
+ // Continue by decoding the vector elements as an array.
+ *frame = Frame(frame->vector_state.element, size,
+ frame->vector_state.element_size, frame->offset);
+ } else {
+ // If there is no element type pointer, there is
+ // nothing to decode in the vector secondary
+ // payload. So just continue.
+ Pop();
+ }
+ continue;
+ }
+ case Frame::kStateDone: {
+ if (out_of_line_offset_ != num_bytes()) {
+ SetError("message did not decode all provided bytes");
+ }
+ return;
+ }
+ }
+ }
+
+#undef FIDL_POP_AND_CONTINUE_OR_RETURN
+ }
+
+protected:
+ void SetError(const char* error_msg) {
+ derived()->SetError(error_msg);
+ }
+
+ template <typename T>
+ typename SetPtrConst<!kMutating, T>::type TypedAt(uint32_t offset) const {
+ return reinterpret_cast<typename SetPtrConst<!kMutating, T>::type>(bytes() + offset);
+ }
+
+ enum class PointerState : uintptr_t {
+ PRESENT = FIDL_ALLOC_PRESENT,
+ ABSENT = FIDL_ALLOC_ABSENT,
+ INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value.
+ };
+
+ enum class HandleState : zx_handle_t {
+ PRESENT = FIDL_HANDLE_PRESENT,
+ ABSENT = FIDL_HANDLE_ABSENT,
+ INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value.
+ };
+
+ uint32_t handle_idx() const { return handle_idx_; }
+
+private:
+ Derived* derived() {
+ return static_cast<Derived*>(this);
+ }
+
+ const Derived* derived() const {
+ return static_cast<const Derived*>(this);
+ }
+
+ // Returns a pointer to the bytes in the message.
+ auto bytes() const {
+ return derived()->bytes();
+ }
+
+ // Returns the number of bytes in the message.
+ auto num_bytes() const {
+ return derived()->num_bytes();
+ }
+
+ // Returns the number of handles in the message (encoding: the max number of handles in the message).
+ auto num_handles() const {
+ return derived()->num_handles();
+ }
+
+ // Returns PRESENT/ABSENT/INVALID for a given pointer value.
+ PointerState GetPointerState(const void* ptr) const {
+ return derived()->GetPointerState(ptr);
+ }
+
+ // Returns PRESENT/ABSENT/INVALID for a given handle value.
+ HandleState GetHandleState(zx_handle_t p) const {
+ return derived()->GetHandleState(p);
+ }
+
+ // If required: mutate a pointer to the dual representation.
+ template <class T2, class T1>
+ void UpdatePointer(T2 p, T1 v) {
+ derived()->UpdatePointer(p, v);
+ }
+
+ // Returns true when a handle was claimed, and false when the
+ // handles are exhausted.
+ template <class ZxHandleTPointer>
+ bool ClaimHandle(ZxHandleTPointer out_handle) {
+ if (handle_idx_ == num_handles()) {
+ derived()->UnclaimedHandle(out_handle);
+ return false;
+ }
+ derived()->ClaimedHandle(out_handle, handle_idx_);
+ ++handle_idx_;
+ return true;
+ }
+
+ // Returns true when the buffer space is claimed, and false when
+ // the requested claim is too large for bytes_.
+ bool ClaimOutOfLineStorage(uint32_t size, const void* storage, uint32_t* out_offset) {
+ if (!derived()->ValidateOutOfLineStorageClaim(storage, &bytes()[out_of_line_offset_])) {
+ return false;
+ }
+
+ // We have to manually maintain alignment here. For example, a pointer
+ // to a struct that is 4 bytes still needs to advance the next
+ // out-of-line offset by 8 to maintain the aligned-to-FIDL_ALIGNMENT
+ // property.
+ static constexpr uint32_t mask = FIDL_ALIGNMENT - 1;
+ uint32_t offset = out_of_line_offset_;
+ if (add_overflow(offset, size, &offset) ||
+ add_overflow(offset, mask, &offset)) {
+ return false;
+ }
+ offset &= ~mask;
+
+ if (offset > num_bytes()) {
+ return false;
+ }
+ *out_offset = out_of_line_offset_;
+ out_of_line_offset_ = offset;
+ return true;
+ }
+
+ uint32_t TypeSize(const fidl_type_t* type) {
+ switch (type->type_tag) {
+ case fidl::kFidlTypeStructPointer:
+ case fidl::kFidlTypeTablePointer:
+ case fidl::kFidlTypeUnionPointer:
+ return sizeof(uint64_t);
+ case fidl::kFidlTypeHandle:
+ return sizeof(zx_handle_t);
+ case fidl::kFidlTypeStruct:
+ return type->coded_struct.size;
+ case fidl::kFidlTypeTable:
+ return sizeof(fidl_vector_t);
+ case fidl::kFidlTypeUnion:
+ return type->coded_union.size;
+ case fidl::kFidlTypeString:
+ return sizeof(fidl_string_t);
+ case fidl::kFidlTypeArray:
+ return type->coded_array.array_size;
+ case fidl::kFidlTypeVector:
+ return sizeof(fidl_vector_t);
+ }
+ abort();
+ return 0;
+ }
+
+ // Functions that manipulate the decoding stack frames.
+ struct Frame {
+ Frame(const fidl_type_t* fidl_type, uint32_t offset)
+ : offset(offset) {
+ switch (fidl_type->type_tag) {
+ case fidl::kFidlTypeStruct:
+ state = kStateStruct;
+ struct_state.fields = fidl_type->coded_struct.fields;
+ struct_state.field_count = fidl_type->coded_struct.field_count;
+ break;
+ case fidl::kFidlTypeStructPointer:
+ state = kStateStructPointer;
+ struct_pointer_state.struct_type = fidl_type->coded_struct_pointer.struct_type;
+ break;
+ case fidl::kFidlTypeTable:
+ state = kStateTable;
+ table_state.fields = fidl_type->coded_table.fields;
+ table_state.field_count = fidl_type->coded_table.field_count;
+ table_state.present_count = 0;
+ break;
+ case fidl::kFidlTypeTablePointer:
+ state = kStateTablePointer;
+ table_pointer_state.table_type = fidl_type->coded_table_pointer.table_type;
+ break;
+ case fidl::kFidlTypeUnion:
+ state = kStateUnion;
+ union_state.types = fidl_type->coded_union.types;
+ union_state.type_count = fidl_type->coded_union.type_count;
+ union_state.data_offset = fidl_type->coded_union.data_offset;
+ break;
+ case fidl::kFidlTypeUnionPointer:
+ state = kStateUnionPointer;
+ union_pointer_state.union_type = fidl_type->coded_union_pointer.union_type;
+ break;
+ case fidl::kFidlTypeArray:
+ state = kStateArray;
+ array_state.element = fidl_type->coded_array.element;
+ array_state.array_size = fidl_type->coded_array.array_size;
+ array_state.element_size = fidl_type->coded_array.element_size;
+ break;
+ case fidl::kFidlTypeString:
+ state = kStateString;
+ string_state.max_size = fidl_type->coded_string.max_size;
+ string_state.nullable = fidl_type->coded_string.nullable;
+ break;
+ case fidl::kFidlTypeHandle:
+ state = kStateHandle;
+ handle_state.nullable = fidl_type->coded_handle.nullable;
+ break;
+ case fidl::kFidlTypeVector:
+ state = kStateVector;
+ vector_state.element = fidl_type->coded_vector.element;
+ vector_state.max_count = fidl_type->coded_vector.max_count;
+ vector_state.element_size = fidl_type->coded_vector.element_size;
+ vector_state.nullable = fidl_type->coded_vector.nullable;
+ break;
+ }
+ }
+
+ Frame(const fidl::FidlCodedStruct* coded_struct, uint32_t offset)
+ : offset(offset) {
+ state = kStateStruct;
+ struct_state.fields = coded_struct->fields;
+ struct_state.field_count = coded_struct->field_count;
+ }
+
+ Frame(const fidl::FidlCodedTable* coded_table, uint32_t offset)
+ : offset(offset) {
+ state = kStateStruct;
+ table_state.fields = coded_table->fields;
+ table_state.field_count = coded_table->field_count;
+ }
+
+ Frame(const fidl::FidlCodedUnion* coded_union, uint32_t offset)
+ : offset(offset) {
+ state = kStateUnion;
+ union_state.types = coded_union->types;
+ union_state.type_count = coded_union->type_count;
+ union_state.data_offset = coded_union->data_offset;
+ }
+
+ Frame(const fidl_type_t* element, uint32_t array_size, uint32_t element_size,
+ uint32_t offset)
+ : offset(offset) {
+ state = kStateArray;
+ array_state.element = element;
+ array_state.array_size = array_size;
+ array_state.element_size = element_size;
+ }
+
+ // The default constructor does nothing when initializing the stack of frames.
+ Frame() {}
+
+ static Frame DoneSentinel() {
+ Frame frame;
+ frame.state = kStateDone;
+ return frame;
+ }
+
+ uint32_t NextStructField() {
+ ZX_DEBUG_ASSERT(state == kStateStruct);
+
+ uint32_t current = field;
+ field += 1;
+ return current;
+ }
+
+ uint32_t NextArrayOffset() {
+ ZX_DEBUG_ASSERT(state == kStateArray);
+
+ uint32_t current = field;
+ field += array_state.element_size;
+ return current;
+ }
+
+ enum : int {
+ kStateStruct,
+ kStateStructPointer,
+ kStateTable,
+ kStateTablePointer,
+ kStateUnion,
+ kStateUnionPointer,
+ kStateArray,
+ kStateString,
+ kStateHandle,
+ kStateVector,
+
+ kStateDone,
+ } state;
+ // A byte offset into bytes_;
+ uint32_t offset;
+
+ // This is a subset of the information recorded in the
+ // fidl_type structures needed for decoding state. For
+ // example, struct sizes do not need to be present here.
+ union {
+ struct {
+ const fidl::FidlField* fields;
+ uint32_t field_count;
+ } struct_state;
+ struct {
+ const fidl::FidlCodedStruct* struct_type;
+ } struct_pointer_state;
+ struct {
+ const fidl::FidlTableField* fields;
+ uint32_t known_index;
+ uint32_t field_count;
+ uint32_t present_count;
+ uint32_t end_offset;
+ uint32_t end_handle;
+ } table_state;
+ struct {
+ const fidl::FidlCodedTable* table_type;
+ } table_pointer_state;
+ struct {
+ const fidl_type_t* const* types;
+ uint32_t type_count;
+ uint32_t data_offset;
+ } union_state;
+ struct {
+ const fidl::FidlCodedUnion* union_type;
+ } union_pointer_state;
+ struct {
+ const fidl_type_t* element;
+ uint32_t array_size;
+ uint32_t element_size;
+ } array_state;
+ struct {
+ uint32_t max_size;
+ bool nullable;
+ } string_state;
+ struct {
+ bool nullable;
+ } handle_state;
+ struct {
+ const fidl_type* element;
+ uint32_t max_count;
+ uint32_t element_size;
+ bool nullable;
+ } vector_state;
+ };
+
+ uint32_t field = 0u;
+ };
+
+ // Returns true on success and false on recursion overflow.
+ bool Push(Frame frame) {
+ if (depth_ == FIDL_RECURSION_DEPTH) {
+ return false;
+ }
+ decoding_frames_[depth_] = frame;
+ ++depth_;
+ return true;
+ }
+
+ void Pop() {
+ ZX_DEBUG_ASSERT(depth_ != 0u);
+ --depth_;
+ }
+
+ Frame* Peek() {
+ ZX_DEBUG_ASSERT(depth_ != 0u);
+ return &decoding_frames_[depth_ - 1];
+ }
+
+ // Message state passed in to the constructor.
+ const fidl_type_t* const type_;
+
+ // Internal state.
+ uint32_t handle_idx_ = 0u;
+ uint32_t out_of_line_offset_ = 0u;
+
+ // Decoding stack state.
+ uint32_t depth_ = 0u;
+ Frame decoding_frames_[FIDL_RECURSION_DEPTH];
+};
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl/builder.cpp b/pkg/fidl/builder.cpp
new file mode 100644
index 0000000..476b7ee
--- /dev/null
+++ b/pkg/fidl/builder.cpp
@@ -0,0 +1,62 @@
+// 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/fidl/cpp/builder.h>
+
+#include <string.h>
+
+#include <lib/fidl/internal.h>
+
+namespace fidl {
+
+Builder::Builder()
+ : capacity_(0u), at_(0u), buffer_(nullptr) {}
+
+Builder::Builder(void* buffer, uint32_t capacity)
+ : capacity_(capacity), at_(0u), buffer_(static_cast<uint8_t*>(buffer)) {
+}
+
+Builder::~Builder() = default;
+
+Builder::Builder(Builder&& other)
+ : capacity_(other.capacity_),
+ at_(other.at_),
+ buffer_(other.buffer_) {
+ other.Reset(nullptr, 0);
+}
+
+Builder& Builder::operator=(Builder&& other) {
+ if (this != &other) {
+ capacity_ = other.capacity_;
+ at_ = other.at_;
+ buffer_ = other.buffer_;
+ other.Reset(nullptr, 0);
+ }
+ return *this;
+}
+
+void* Builder::Allocate(uint32_t size) {
+ uint64_t limit = FidlAlign(at_ + size);
+ if (limit > capacity_)
+ return nullptr;
+ uint8_t* result = &buffer_[at_];
+ memset(buffer_ + at_, 0, limit - at_);
+ at_ = static_cast<uint32_t>(limit);
+ return result;
+}
+
+BytePart Builder::Finalize() {
+ BytePart bytes(buffer_, capacity_, at_);
+ capacity_ = 0u;
+ at_ = 0u;
+ return bytes;
+}
+
+void Builder::Reset(void* buffer, uint32_t capacity) {
+ buffer_ = static_cast<uint8_t*>(buffer);
+ capacity_ = capacity;
+ at_ = 0u;
+}
+
+} // namespace fidl
diff --git a/pkg/fidl/decoding.cpp b/pkg/fidl/decoding.cpp
new file mode 100644
index 0000000..3066778
--- /dev/null
+++ b/pkg/fidl/decoding.cpp
@@ -0,0 +1,115 @@
+// Copyright 2017 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/fidl/coding.h>
+
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
+#include "buffer_walker.h"
+
+// TODO(kulakowski) Design zx_status_t error values.
+
+namespace {
+
+class FidlDecoder final : public fidl::internal::BufferWalker<FidlDecoder, true, false> {
+ typedef fidl::internal::BufferWalker<FidlDecoder, true, false> Super;
+
+public:
+ FidlDecoder(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const zx_handle_t* handles, uint32_t num_handles, const char** out_error_msg)
+ : Super(type), bytes_(static_cast<uint8_t*>(bytes)), num_bytes_(num_bytes),
+ handles_(handles), num_handles_(num_handles), out_error_msg_(out_error_msg) {}
+
+ void Walk() {
+ if (handles_ == nullptr && num_handles_ != 0u) {
+ SetError("Cannot provide non-zero handle count and null handle pointer");
+ return;
+ }
+ Super::Walk();
+ if (status_ == ZX_OK && handle_idx() != num_handles()) {
+ SetError("message did not contain the specified number of handles");
+ }
+ }
+
+ uint8_t* bytes() const { return bytes_; }
+ uint32_t num_bytes() const { return num_bytes_; }
+ uint32_t num_handles() const { return num_handles_; }
+
+ bool ValidateOutOfLineStorageClaim(const void* a, const void* b) {
+ return true;
+ }
+
+ void UnclaimedHandle(zx_handle_t* out_handle) {}
+ void ClaimedHandle(zx_handle_t* out_handle, uint32_t idx) {
+ if (out_handle != nullptr) {
+ *out_handle = handles_[idx];
+#ifdef __Fuchsia__
+ } else {
+ // Return value intentionally ignored: this is best-effort cleanup.
+ zx_handle_close(handles_[idx]);
+#endif
+ }
+ }
+
+ PointerState GetPointerState(const void* ptr) const {
+ return static_cast<PointerState>(*static_cast<const uintptr_t*>(ptr));
+ }
+ HandleState GetHandleState(zx_handle_t p) const {
+ return static_cast<HandleState>(p);
+ }
+
+ template <class T>
+ void UpdatePointer(T* p, T v) {
+ *p = v;
+ }
+
+ void SetError(const char* error_msg) {
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error_msg;
+ }
+#ifdef __Fuchsia__
+ if (handles_) {
+ // Return value intentionally ignored: this is best-effort cleanup.
+ zx_handle_close_many(handles_, num_handles());
+ }
+#endif
+ }
+
+ zx_status_t status() const { return status_; }
+
+private:
+ uint8_t* const bytes_;
+ const uint32_t num_bytes_;
+ const zx_handle_t* const handles_;
+ const uint32_t num_handles_;
+ const char** const out_error_msg_;
+ zx_status_t status_ = ZX_OK;
+};
+
+} // namespace
+
+zx_status_t fidl_decode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const zx_handle_t* handles, uint32_t num_handles,
+ const char** out_error_msg) {
+ FidlDecoder decoder(type, bytes, num_bytes, handles, num_handles, out_error_msg);
+ decoder.Walk();
+ return decoder.status();
+}
+
+zx_status_t fidl_decode_msg(const fidl_type_t* type, fidl_msg_t* msg,
+ const char** out_error_msg) {
+ return fidl_decode(type, msg->bytes, msg->num_bytes, msg->handles,
+ msg->num_handles, out_error_msg);
+}
diff --git a/pkg/fidl/encoding.cpp b/pkg/fidl/encoding.cpp
new file mode 100644
index 0000000..f4d4f49
--- /dev/null
+++ b/pkg/fidl/encoding.cpp
@@ -0,0 +1,134 @@
+// Copyright 2017 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/fidl/coding.h>
+
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
+#include "buffer_walker.h"
+
+#include <stdio.h>
+
+// TODO(kulakowski) Design zx_status_t error values.
+
+namespace {
+
+class FidlEncoder final : public fidl::internal::BufferWalker<FidlEncoder, true, true> {
+ typedef fidl::internal::BufferWalker<FidlEncoder, true, true> Super;
+
+public:
+ FidlEncoder(const fidl_type_t* type, void* bytes, uint32_t num_bytes, zx_handle_t* handles,
+ uint32_t num_handles, uint32_t* out_actual_handles, const char** out_error_msg)
+ : Super(type), bytes_(static_cast<uint8_t*>(bytes)), num_bytes_(num_bytes),
+ handles_(handles), num_handles_(num_handles), out_actual_handles_(out_actual_handles),
+ out_error_msg_(out_error_msg) {}
+
+ void Walk() {
+ if (handles_ == nullptr && num_handles_ != 0u) {
+ SetError("Cannot provide non-zero handle count and null handle pointer");
+ return;
+ }
+ if (out_actual_handles_ == nullptr) {
+ SetError("Cannot encode with null out_actual_handles");
+ return;
+ }
+ Super::Walk();
+ if (status_ == ZX_OK) {
+ *out_actual_handles_ = handle_idx();
+ }
+ }
+
+ uint8_t* bytes() const { return bytes_; }
+ uint32_t num_bytes() const { return num_bytes_; }
+ uint32_t num_handles() const { return num_handles_; }
+
+ bool ValidateOutOfLineStorageClaim(const void* a, const void* b) {
+ return a == b;
+ }
+
+ void UnclaimedHandle(zx_handle_t* out_handle) {
+#ifdef __Fuchsia__
+ // Return value intentionally ignored: this is best-effort cleanup.
+ zx_handle_close(*out_handle);
+#endif
+ }
+ void ClaimedHandle(zx_handle_t* out_handle, uint32_t idx) {
+ assert(out_handle != nullptr);
+ handles_[idx] = *out_handle;
+ *out_handle = FIDL_HANDLE_PRESENT;
+ }
+
+ PointerState GetPointerState(const void* ptr) const {
+ return *static_cast<const uintptr_t*>(ptr) == 0
+ ? PointerState::ABSENT
+ : PointerState::PRESENT;
+ }
+ HandleState GetHandleState(zx_handle_t p) const {
+ return p == ZX_HANDLE_INVALID
+ ? HandleState::ABSENT
+ : HandleState::PRESENT;
+ }
+
+ template <class T>
+ void UpdatePointer(T** p, T* v) {
+ assert(*p == v);
+ assert(v != nullptr);
+ *p = reinterpret_cast<T*>(FIDL_ALLOC_PRESENT);
+ }
+
+ void SetError(const char* error_msg) {
+ if (status_ != ZX_OK) {
+ return;
+ }
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error_msg;
+ }
+#ifdef __Fuchsia__
+ if (handles_) {
+ // Return value intentionally ignored: this is best-effort cleanup.
+ zx_handle_close_many(handles_, num_handles());
+ }
+#endif
+ }
+
+ zx_status_t status() const { return status_; }
+
+private:
+ // Message state passed in to the constructor.
+ uint8_t* const bytes_;
+ const uint32_t num_bytes_;
+ zx_handle_t* const handles_;
+ const uint32_t num_handles_;
+ uint32_t* const out_actual_handles_;
+ const char** const out_error_msg_;
+ zx_status_t status_ = ZX_OK;
+};
+
+} // namespace
+
+zx_status_t fidl_encode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ zx_handle_t* handles, uint32_t max_handles, uint32_t* out_actual_handles,
+ const char** out_error_msg) {
+ FidlEncoder encoder(type, bytes, num_bytes, handles, max_handles, out_actual_handles,
+ out_error_msg);
+ encoder.Walk();
+ return encoder.status();
+}
+
+zx_status_t fidl_encode_msg(const fidl_type_t* type, fidl_msg_t* msg,
+ uint32_t* out_actual_handles, const char** out_error_msg) {
+ return fidl_encode(type, msg->bytes, msg->num_bytes, msg->handles, msg->num_handles,
+ out_actual_handles, out_error_msg);
+}
diff --git a/pkg/fidl/epitaph.c b/pkg/fidl/epitaph.c
new file mode 100644
index 0000000..d1a14e1
--- /dev/null
+++ b/pkg/fidl/epitaph.c
@@ -0,0 +1,22 @@
+// 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.
+
+#ifdef __Fuchsia__
+
+#include <string.h>
+
+#include <lib/fidl/epitaph.h>
+#include <zircon/fidl.h>
+#include <zircon/syscalls.h>
+
+zx_status_t fidl_epitaph_write(zx_handle_t channel, zx_status_t error) {
+ fidl_epitaph_t epitaph;
+ memset(&epitaph, 0, sizeof(epitaph));
+ epitaph.hdr.ordinal = FIDL_EPITAPH_ORDINAL;
+ epitaph.hdr.reserved0 = error;
+
+ return zx_channel_write(channel, 0, &epitaph, sizeof(epitaph), NULL, 0);
+}
+
+#endif // __Fuchsia__
diff --git a/pkg/fidl/formatting.cpp b/pkg/fidl/formatting.cpp
new file mode 100644
index 0000000..ffe6f39
--- /dev/null
+++ b/pkg/fidl/formatting.cpp
@@ -0,0 +1,218 @@
+// Copyright 2017 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/fidl/coding.h>
+
+#include <inttypes.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+namespace {
+
+class StringBuilder {
+public:
+ StringBuilder(char* buffer, size_t capacity)
+ : buffer_(buffer), capacity_(capacity) {}
+
+ size_t length() const { return length_; }
+
+ void Append(const char* data, size_t length) {
+ size_t remaining = capacity_ - length_;
+ if (length > remaining) {
+ length = remaining;
+ }
+ memcpy(buffer_ + length_, data, length);
+ length_ += length;
+ }
+
+ void Append(const char* data) {
+ Append(data, strlen(data));
+ }
+
+ void AppendPrintf(const char* format, ...) __PRINTFLIKE(2, 3) {
+ va_list ap;
+ va_start(ap, format);
+ AppendVPrintf(format, ap);
+ va_end(ap);
+ }
+
+ void AppendVPrintf(const char* format, va_list ap) {
+ size_t remaining = capacity_ - length_;
+ if (remaining == 0u) {
+ return;
+ }
+ int count = vsnprintf(buffer_ + length_, remaining, format, ap);
+ if (count <= 0) {
+ return;
+ }
+ size_t length = static_cast<size_t>(count);
+ length_ += (length >= remaining ? remaining : length);
+ }
+
+private:
+ char* buffer_;
+ size_t capacity_;
+ size_t length_ = 0u;
+};
+
+void FormatNullability(StringBuilder* str, fidl::FidlNullability nullable) {
+ if (nullable == fidl::kNullable) {
+ str->Append("?");
+ }
+}
+
+void FormatStructName(StringBuilder* str, const fidl::FidlCodedStruct* coded_struct) {
+ if (coded_struct->name) {
+ str->Append(coded_struct->name);
+ } else {
+ str->Append("struct");
+ }
+}
+
+void FormatUnionName(StringBuilder* str, const fidl::FidlCodedUnion* coded_union) {
+ if (coded_union->name) {
+ str->Append(coded_union->name);
+ } else {
+ str->Append("union");
+ }
+}
+
+void FormatTypeName(StringBuilder* str, const fidl_type_t* type);
+void FormatElementName(StringBuilder* str, const fidl_type_t* type) {
+ if (type) {
+ FormatTypeName(str, type);
+ } else {
+ // TODO(jeffbrown): Print the actual primitive type name, assuming we
+ // start recording that information in the tables.
+ str->Append("primitive");
+ }
+}
+
+void FormatTypeName(StringBuilder* str, const fidl_type_t* type) {
+ switch (type->type_tag) {
+ case fidl::kFidlTypeStruct:
+ FormatStructName(str, &type->coded_struct);
+ break;
+ case fidl::kFidlTypeStructPointer:
+ FormatStructName(str, type->coded_struct_pointer.struct_type);
+ str->Append("?");
+ break;
+ case fidl::kFidlTypeUnion:
+ FormatUnionName(str, &type->coded_union);
+ break;
+ case fidl::kFidlTypeUnionPointer:
+ FormatUnionName(str, type->coded_union_pointer.union_type);
+ str->Append("?");
+ break;
+ case fidl::kFidlTypeArray:
+ str->Append("array<");
+ FormatElementName(str, type->coded_array.element);
+ str->Append(">");
+ str->AppendPrintf(":%" PRIu32, type->coded_array.array_size /
+ type->coded_array.element_size);
+ break;
+ case fidl::kFidlTypeString:
+ str->Append("string");
+ if (type->coded_string.max_size != FIDL_MAX_SIZE) {
+ str->AppendPrintf(":%" PRIu32, type->coded_string.max_size);
+ }
+ FormatNullability(str, type->coded_string.nullable);
+ break;
+ case fidl::kFidlTypeHandle:
+ str->Append("handle");
+ if (type->coded_handle.handle_subtype) {
+ str->Append("<");
+ switch (type->coded_handle.handle_subtype) {
+ case fidl::kFidlHandleSubtypeHandle:
+ str->Append("handle");
+ break;
+ case fidl::kFidlHandleSubtypeProcess:
+ str->Append("process");
+ break;
+ case fidl::kFidlHandleSubtypeThread:
+ str->Append("thread");
+ break;
+ case fidl::kFidlHandleSubtypeVmo:
+ str->Append("vmo");
+ break;
+ case fidl::kFidlHandleSubtypeChannel:
+ str->Append("channel");
+ break;
+ case fidl::kFidlHandleSubtypeEvent:
+ str->Append("event");
+ break;
+ case fidl::kFidlHandleSubtypePort:
+ str->Append("port");
+ break;
+ case fidl::kFidlHandleSubtypeInterrupt:
+ str->Append("interrupt");
+ break;
+ case fidl::kFidlHandleSubtypeLog:
+ str->Append("log");
+ break;
+ case fidl::kFidlHandleSubtypeSocket:
+ str->Append("socket");
+ break;
+ case fidl::kFidlHandleSubtypeResource:
+ str->Append("resource");
+ break;
+ case fidl::kFidlHandleSubtypeEventpair:
+ str->Append("eventpair");
+ break;
+ case fidl::kFidlHandleSubtypeJob:
+ str->Append("job");
+ break;
+ case fidl::kFidlHandleSubtypeVmar:
+ str->Append("vmar");
+ break;
+ case fidl::kFidlHandleSubtypeFifo:
+ str->Append("fifo");
+ break;
+ case fidl::kFidlHandleSubtypeGuest:
+ str->Append("guest");
+ break;
+ case fidl::kFidlHandleSubtypeTimer:
+ str->Append("timer");
+ break;
+ // TODO(pascallouis): Add support for iomap, pci, and hypervisor
+ // when they are supported in FIDL.
+ default:
+ str->AppendPrintf("%" PRIu32, type->coded_handle.handle_subtype);
+ break;
+ }
+ str->Append(">");
+ }
+ FormatNullability(str, type->coded_handle.nullable);
+ break;
+ case fidl::kFidlTypeVector:
+ str->Append("vector<");
+ FormatElementName(str, type->coded_vector.element);
+ str->Append(">");
+ if (type->coded_vector.max_count != FIDL_MAX_SIZE) {
+ str->AppendPrintf(":%" PRIu32, type->coded_vector.max_count);
+ }
+ FormatNullability(str, type->coded_vector.nullable);
+ break;
+ default:
+ ZX_PANIC("unrecognized tag");
+ break;
+ }
+}
+
+} // namespace
+
+size_t fidl_format_type_name(const fidl_type_t* type,
+ char* buffer, size_t capacity) {
+ if (!type || !buffer || !capacity) {
+ return 0u;
+ }
+
+ StringBuilder str(buffer, capacity);
+ FormatTypeName(&str, type);
+ return str.length();
+}
diff --git a/pkg/fidl/handle_closing.cpp b/pkg/fidl/handle_closing.cpp
new file mode 100644
index 0000000..5350993
--- /dev/null
+++ b/pkg/fidl/handle_closing.cpp
@@ -0,0 +1,108 @@
+// 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 <limits>
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <lib/fidl/coding.h>
+
+#ifdef __Fuchsia__
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+#include <zircon/syscalls.h>
+
+#include "buffer_walker.h"
+
+namespace {
+
+class FidlHandleCloser final : public fidl::internal::BufferWalker<FidlHandleCloser, true, true> {
+ typedef fidl::internal::BufferWalker<FidlHandleCloser, true, true> Super;
+
+public:
+ FidlHandleCloser(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const char** out_error_msg)
+ : Super(type), bytes_(static_cast<uint8_t*>(bytes)), num_bytes_(num_bytes),
+ out_error_msg_(out_error_msg) {}
+
+ void Walk() {
+ Super::Walk();
+ }
+
+ uint8_t* bytes() const { return bytes_; }
+ uint32_t num_bytes() const { return num_bytes_; }
+ uint32_t num_handles() const { return std::numeric_limits<uint32_t>::max(); }
+
+ bool ValidateOutOfLineStorageClaim(const void* a, void* b) {
+ return true;
+ }
+
+ void UnclaimedHandle(zx_handle_t* out_handle) {
+ // This will never happen since we are returning numeric_limits::max() in num_handles.
+ // We want to claim (close) all the handles.
+ ZX_DEBUG_ASSERT(false);
+ }
+
+ void ClaimedHandle(zx_handle_t* out_handle, uint32_t idx) {
+ if (*out_handle != ZX_HANDLE_INVALID) {
+ zx_handle_close(*out_handle);
+ }
+ *out_handle = ZX_HANDLE_INVALID;
+ }
+
+ template <class T>
+ void UpdatePointer(T* const* p, T* v) {}
+
+ PointerState GetPointerState(const void* ptr) const {
+ return *static_cast<const uintptr_t*>(ptr) == 0
+ ? PointerState::ABSENT
+ : PointerState::PRESENT;
+ }
+
+ HandleState GetHandleState(zx_handle_t p) const {
+ // Treat all handles as present to keep the buffer walker going.
+ return HandleState::PRESENT;
+ }
+
+ void SetError(const char* error_msg) {
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error_msg;
+ }
+ }
+
+ zx_status_t status() const { return status_; }
+
+private:
+ // Message state passed in to the constructor.
+ uint8_t* const bytes_;
+ const uint32_t num_bytes_;
+ const char** const out_error_msg_;
+ zx_status_t status_ = ZX_OK;
+};
+
+} // namespace
+
+#endif // __Fuchsia__
+
+zx_status_t fidl_close_handles(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const char** out_error_msg) {
+#if __Fuchsia__
+ FidlHandleCloser handle_closer(type, bytes, num_bytes, out_error_msg);
+ handle_closer.Walk();
+ return handle_closer.status();
+#else
+ return ZX_OK; // there can't be any handles on the host
+#endif
+}
+
+zx_status_t fidl_close_handles_msg(const fidl_type_t* type, const fidl_msg_t* msg,
+ const char** out_error_msg) {
+ return fidl_close_handles(type, msg->bytes, msg->num_bytes, out_error_msg);
+}
+
diff --git a/pkg/fidl/include/lib/fidl/coding.h b/pkg/fidl/include/lib/fidl/coding.h
new file mode 100644
index 0000000..f30fe94
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/coding.h
@@ -0,0 +1,60 @@
+// Copyright 2017 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.
+
+#ifndef LIB_FIDL_CODING_H_
+#define LIB_FIDL_CODING_H_
+
+#include <zircon/compiler.h>
+#include <zircon/fidl.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+// The maximum recursion depth the fidl encoder or decoder will
+// perform. Each nested aggregate type (structs, unions, arrays, or
+// vectors) counts as one step in the recursion depth.
+#define FIDL_RECURSION_DEPTH 32
+
+// See https://fuchsia.googlesource.com/docs/+/master/development/languages/fidl/languages/c.md#fidl_encode-fidl_encode_msg
+zx_status_t fidl_encode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ zx_handle_t* handles, uint32_t max_handles,
+ uint32_t* out_actual_handles, const char** out_error_msg);
+zx_status_t fidl_encode_msg(const fidl_type_t* type, fidl_msg_t* msg,
+ uint32_t* out_actual_handles, const char** out_error_msg);
+
+// See https://fuchsia.googlesource.com/docs/+/master/development/languages/fidl/languages/c.md#fidl_decode-fidl_decode_msg
+zx_status_t fidl_decode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const zx_handle_t* handles, uint32_t num_handles,
+ const char** error_msg_out);
+zx_status_t fidl_decode_msg(const fidl_type_t* type, fidl_msg_t* msg,
+ const char** out_error_msg);
+
+// Validates an encoded message against the given |type|.
+//
+// The |bytes| are not modified.
+zx_status_t fidl_validate(const fidl_type_t* type, const void* bytes, uint32_t num_bytes,
+ uint32_t num_handles, const char** error_msg_out);
+zx_status_t fidl_validate_msg(const fidl_type_t* type, const fidl_msg_t* msg,
+ const char** out_error_msg);
+
+// Traverses a linearized FIDL message, closing all handles within it.
+// This function is a no-op on host side.
+//
+// Handle values in |bytes| are replaced with ZX_HANDLE_INVALID.
+zx_status_t fidl_close_handles(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
+ const char** out_error_msg);
+zx_status_t fidl_close_handles_msg(const fidl_type_t* type, const fidl_msg_t* msg,
+ const char** out_error_msg);
+
+// Stores the name of a fidl type into the provided buffer.
+// Truncates the name if it is too long to fit into the buffer.
+// Returns the number of characters written into the buffer.
+//
+// Note: This function does not write a trailing NUL.
+size_t fidl_format_type_name(const fidl_type_t* type,
+ char* buffer, size_t capacity);
+
+__END_CDECLS
+
+#endif // LIB_FIDL_CODING_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/builder.h b/pkg/fidl/include/lib/fidl/cpp/builder.h
new file mode 100644
index 0000000..bc394c5
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/builder.h
@@ -0,0 +1,104 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_BUILDER_H_
+#define LIB_FIDL_CPP_BUILDER_H_
+
+#include <new> // For placement new.
+#include <stdalign.h>
+#include <stdint.h>
+
+#include <lib/fidl/cpp/message_part.h>
+#include <zircon/compiler.h>
+#include <zircon/fidl.h>
+#include <zircon/types.h>
+
+namespace fidl {
+
+// Builder helps FIDL clients store decoded objects in a buffer.
+//
+// Objects are allocated sequentually in the buffer with appropriate alignment
+// for in-place encoding. The client is responsible for ordering the objects in
+// the buffer appropriately.
+class Builder {
+public:
+ // Creates a buffer without any storage.
+ Builder();
+
+ // Creates a buffer that stores objects in the given memory.
+ //
+ // The constructed |Builder| object does not take ownership of the given
+ // storage.
+ Builder(void* buffer, uint32_t capacity);
+ ~Builder();
+
+ Builder(const Builder& other) = delete;
+ Builder& operator=(const Builder& other) = delete;
+
+ Builder(Builder&& other);
+ Builder& operator=(Builder&& other);
+
+ // Allocates storage in the buffer of sufficient size to store an object of
+ // type |T|. The object must have alignment constraints that are compatible
+ // with FIDL messages.
+ //
+ // If there is insufficient storage in the builder's buffer, this method
+ // returns nullptr.
+ template <typename T>
+ T* New() {
+ static_assert(alignof(T) <= FIDL_ALIGNMENT, "");
+ static_assert(sizeof(T) <= ZX_CHANNEL_MAX_MSG_BYTES, "");
+ if (void* ptr = Allocate(sizeof(T)))
+ return new (ptr) T;
+ return nullptr;
+ }
+
+ // Allocates storage in the buffer of sufficient size to store |count|
+ // objects of type |T|. The object must have alignment constraints that are
+ // compatible with FIDL messages.
+ //
+ // If there is insufficient storage in the builder's buffer, this method
+ // returns nullptr.
+ template <typename T>
+ T* NewArray(uint32_t count) {
+ static_assert(alignof(T) <= FIDL_ALIGNMENT, "");
+ static_assert(sizeof(T) <= ZX_CHANNEL_MAX_MSG_BYTES, "");
+ if (sizeof(T) * static_cast<uint64_t>(count) > UINT32_MAX)
+ return nullptr;
+ if (void* ptr = Allocate(static_cast<uint32_t>(sizeof(T) * count)))
+ return new (ptr) T[count];
+ return nullptr;
+ }
+
+ // Completes the building and returns a |MesssagePart| containing the
+ // allocated objects.
+ //
+ // The allocated objects are placed in the returned buffer in the order in
+ // which they were allocated, with appropriate alignment for a FIDL message.
+ // The returned buffer's capacity cooresponds to the capacity originally
+ // provided to this builder in its constructor.
+ BytePart Finalize();
+
+ // Attaches the given storage to the |Builder|.
+ //
+ // The |Builder| object does not take ownership of the given storage. The
+ // next object will be allocated at the start of the buffer.
+ void Reset(void* buffer, uint32_t capacity);
+
+protected:
+ uint8_t* buffer() const { return buffer_; }
+ uint32_t capacity() const { return capacity_; }
+
+private:
+ // Returns |size| bytes of zeroed memory aligned to at least FIDL_ALIGNMENT
+ void* Allocate(uint32_t size);
+
+ uint32_t capacity_;
+ uint32_t at_;
+ uint8_t* buffer_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_BUILDER_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/message.h b/pkg/fidl/include/lib/fidl/cpp/message.h
new file mode 100644
index 0000000..d93e7c9
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/message.h
@@ -0,0 +1,172 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_MESSAGE_H_
+#define LIB_FIDL_CPP_MESSAGE_H_
+
+#include <lib/fidl/coding.h>
+#include <lib/fidl/cpp/message_part.h>
+#include <zircon/fidl.h>
+
+namespace fidl {
+
+// A FIDL message.
+//
+// A FIDL message has two parts: the bytes and the handles. The bytes are
+// divided into a header (of type fidl_message_header_t) and a payload, which
+// follows the header.
+//
+// A Message object does not own the storage for the message parts.
+class Message {
+public:
+ // Creates a message without any storage.
+ Message();
+
+ // Creates a message whose storage is backed by |bytes| and |handles|.
+ //
+ // The constructed |Message| object does not take ownership of the given
+ // storage, although does take ownership of zircon handles contained withing
+ // handles.
+ Message(BytePart bytes, HandlePart handles);
+
+ ~Message();
+
+ Message(const Message& other) = delete;
+ Message& operator=(const Message& other) = delete;
+
+ Message(Message&& other);
+ Message& operator=(Message&& other);
+
+ // Whether the message has enough bytes to contain a fidl_message_header_t.
+ bool has_header() const {
+ return bytes_.actual() >= sizeof(fidl_message_header_t);
+ }
+
+ // The header at the start of the message.
+ //
+ // Valid only if has_header().
+ const fidl_message_header_t& header() const {
+ return *reinterpret_cast<fidl_message_header_t*>(bytes_.data());
+ }
+ fidl_message_header_t& header() {
+ return *reinterpret_cast<fidl_message_header_t*>(bytes_.data());
+ }
+
+ // The transaction ID in the message header.
+ //
+ // Valid only if has_header().
+ zx_txid_t txid() const { return header().txid; }
+ void set_txid(zx_txid_t txid) { header().txid = txid; }
+
+ // The flags in the message header.
+ //
+ // Valid only if has_header().
+ uint32_t flags() const { return header().flags; }
+
+ // The flags in the message header.
+ //
+ // Valid only if has_header().
+ uint32_t ordinal() const { return header().ordinal; }
+
+ // The message payload that follows the header.
+ //
+ // Valid only if has_header().
+ BytePart payload() const {
+ constexpr uint32_t n = sizeof(fidl_message_header_t);
+ return BytePart(bytes_.data() + n, bytes_.capacity() - n, bytes_.actual() - n);
+ }
+
+ // The message bytes interpreted as the given type.
+ template <typename T>
+ T* GetBytesAs() const {
+ return reinterpret_cast<T*>(bytes_.data());
+ }
+
+ // The message payload that follows the header interpreted as the given type.
+ //
+ // Valid only if has_header().
+ template <typename T>
+ T* GetPayloadAs() const {
+ return reinterpret_cast<T*>(bytes_.data() + sizeof(fidl_message_header_t));
+ }
+
+ // The storage for the bytes of the message.
+ BytePart& bytes() { return bytes_; }
+ const BytePart& bytes() const { return bytes_; }
+ void set_bytes(BytePart bytes) { bytes_ = static_cast<BytePart&&>(bytes); }
+
+ // The storage for the handles of the message.
+ //
+ // When the message is encoded, the handle values are stored in this part of
+ // the message. When the message is decoded, this part of the message is
+ // empty and the handle values are stored in the bytes().
+ HandlePart& handles() { return handles_; }
+ const HandlePart& handles() const { return handles_; }
+
+ // Encodes the message in-place.
+ //
+ // The message must previously have been in a decoded state, for example,
+ // either by being built in a decoded state using a |Builder| or having been
+ // decoded using the |Decode| method.
+ zx_status_t Encode(const fidl_type_t* type, const char** error_msg_out);
+
+ // Decodes the message in-place.
+ //
+ // The message must previously have been in an encoded state, for example,
+ // either by being read from a zx_channel_t or having been encoded using the
+ // |Encode| method.
+ zx_status_t Decode(const fidl_type_t* type, const char** error_msg_out);
+
+ // Validates the message in-place.
+ //
+ // The message must already be in an encoded state, for example, either by
+ // being read from a zx_channel_t or having been created in that state.
+ //
+ // Does not modify the message.
+ zx_status_t Validate(const fidl_type_t* type, const char** error_msg_out) const;
+
+ // Read a message from the given channel.
+ //
+ // The bytes read from the channel are stored in bytes() and the handles
+ // read from the channel are stored in handles(). Existing data in these
+ // buffers is overwritten.
+ zx_status_t Read(zx_handle_t channel, uint32_t flags);
+
+ // Writes a message to the given channel.
+ //
+ // The bytes stored in bytes() are written to the channel and the handles
+ // stored in handles() are written to the channel.
+ //
+ // If this method returns ZX_OK, handles() will be empty because they were
+ // consumed by this operation.
+ zx_status_t Write(zx_handle_t channel, uint32_t flags);
+
+ // Issues a synchronous send and receive transaction on the given channel.
+ //
+ // The bytes stored in bytes() are written to the channel and the handles
+ // stored in handles() are written to the channel. The bytes read from the
+ // channel are stored in response->bytes() and the handles read from the
+ // channel are stored in response->handles().
+ //
+ // If this method returns ZX_OK, handles() will be empty because they were
+ // consumed by this operation.
+ zx_status_t Call(zx_handle_t channel, uint32_t flags, zx_time_t deadline,
+ Message* response);
+
+ // Stop tracking the handles in stored in handles(), without closing them.
+ //
+ // Typically, these handles will be extracted during decode or the
+ // message's destructor, so this function will be unnecessary. However,
+ // for clients of ulib/fidl which decode message manually, this function
+ // is necessary to prevent extracted handles from being closed.
+ void ClearHandlesUnsafe();
+
+private:
+ BytePart bytes_;
+ HandlePart handles_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_MESSAGE_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/message_buffer.h b/pkg/fidl/include/lib/fidl/cpp/message_buffer.h
new file mode 100644
index 0000000..8e85823
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/message_buffer.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_MESSAGE_BUFFER_H_
+#define LIB_FIDL_CPP_MESSAGE_BUFFER_H_
+
+#include <stdint.h>
+
+#include <lib/fidl/cpp/builder.h>
+#include <lib/fidl/cpp/message.h>
+#include <zircon/fidl.h>
+#include <zircon/types.h>
+
+namespace fidl {
+
+class MessageBuffer {
+public:
+ // Creates a |MessageBuffer| that allocates buffers for message of the
+ // given capacities.
+ //
+ // The buffers are freed when the |MessageBuffer| is destructed.
+ explicit MessageBuffer(
+ uint32_t bytes_capacity = ZX_CHANNEL_MAX_MSG_BYTES,
+ uint32_t handles_capacity = ZX_CHANNEL_MAX_MSG_HANDLES);
+
+ // The memory that backs the message is freed by this destructor.
+ ~MessageBuffer();
+
+ // The memory in which bytes can be stored in this buffer.
+ uint8_t* bytes() const { return buffer_; }
+
+ // The total number of bytes that can be stored in this buffer.
+ uint32_t bytes_capacity() const { return bytes_capacity_; }
+
+ // The memory in which handles can be stored in this buffer.
+ zx_handle_t* handles() const;
+
+ // The total number of handles that can be stored in this buffer.
+ uint32_t handles_capacity() const { return handles_capacity_; }
+
+ // Creates a |Message| that is backed by the memory in this buffer.
+ //
+ // The returned |Message| contains no bytes or handles.
+ Message CreateEmptyMessage();
+
+ // Creates a |Builder| that is backed by the memory in this buffer.
+ Builder CreateBuilder();
+
+private:
+ uint8_t* const buffer_;
+ const uint32_t bytes_capacity_;
+ const uint32_t handles_capacity_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_MESSAGE_BUFFER_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/message_builder.h b/pkg/fidl/include/lib/fidl/cpp/message_builder.h
new file mode 100644
index 0000000..b571f3b
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/message_builder.h
@@ -0,0 +1,76 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_MESSAGE_BUILDER_H_
+#define LIB_FIDL_CPP_MESSAGE_BUILDER_H_
+
+#include <stdint.h>
+
+#include <lib/fidl/cpp/builder.h>
+#include <lib/fidl/cpp/message_buffer.h>
+#include <lib/fidl/cpp/message.h>
+#include <zircon/fidl.h>
+#include <zircon/types.h>
+
+namespace fidl {
+
+// A builder for FIDL messages that owns the memory for the message.
+//
+// A |MessageBuilder| is a |Builder| that uses the heap to back the memory for
+// the message. If you wish to manage the memory yourself, you can use |Builder|
+// and |Message| directly.
+//
+// Upon creation, the |MessageBuilder| creates a FIDL message header, which you
+// can modify using |header()|.
+class MessageBuilder : public Builder {
+public:
+ // Creates a |MessageBuilder| for the given |type| that allocates buffers
+ // for message of the given capacities.
+ //
+ // The bytes buffer is initialied by adding a |fidl_message_header_t|
+ // header.
+ //
+ // The buffers are freed when the |MessageBuilder| is destructed.
+ explicit MessageBuilder(
+ const fidl_type_t* type,
+ uint32_t bytes_capacity = ZX_CHANNEL_MAX_MSG_BYTES,
+ uint32_t handles_capacity = ZX_CHANNEL_MAX_MSG_HANDLES);
+
+ // The memory that backs the message is freed by this destructor.
+ ~MessageBuilder();
+
+ // The type of the message payload this object is building.
+ const fidl_type_t* type() const { return type_; }
+
+ // The header for the message.
+ //
+ // The message header is allocated by the |MessageBuilder| itself.
+ fidl_message_header_t* header() const {
+ return reinterpret_cast<fidl_message_header_t*>(buffer());
+ }
+
+ // Encodes a message of the given |type|.
+ //
+ // The memory that backs the message returned by this function is owned by
+ // the |MessageBuilder|, which means the |MessageBuilder| must remain alive
+ // as long as the |Message| object is in use.
+ //
+ // The |message| parameter might be modified even if this method returns an
+ // error.
+ zx_status_t Encode(Message* message_out, const char** error_msg_out);
+
+ // Resets all the data in the |MessageBuffer|.
+ //
+ // The underlying buffer is retained and reused. The next object will be
+ // allocated at the start of the buffer.
+ void Reset();
+
+private:
+ const fidl_type_t* type_;
+ MessageBuffer buffer_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_MESSAGE_BUILDER_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/message_part.h b/pkg/fidl/include/lib/fidl/cpp/message_part.h
new file mode 100644
index 0000000..1dd7f7b
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/message_part.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_MESSAGE_PART_H_
+#define LIB_FIDL_CPP_MESSAGE_PART_H_
+
+#include <stdint.h>
+#include <string.h>
+
+#include <zircon/types.h>
+
+namespace fidl {
+
+// Part of a FIDL message.
+//
+// A FIDL message has two parts: the bytes and the handles. This class is used
+// to represent both kinds of parts.
+//
+// Each part of the message has a data buffer, which contains the actual data
+// for that part of the message, a capacity for that buffer, and the actual
+// amount of data stored in the buffer, which might be less that the capacity if
+// the buffer is not completely full.
+template<typename T>
+class MessagePart {
+public:
+ using value_type = T;
+ using const_iterator = const T*;
+
+ // A message part with no storage.
+ MessagePart() : data_(nullptr), capacity_(0u), actual_(0u) {}
+
+ // A message part that uses the given storage.
+ //
+ // The constructed |MessagePart| object does not take ownership of the given
+ // storage.
+ MessagePart(T* data, uint32_t capacity, uint32_t actual = 0u)
+ : data_(data), capacity_(capacity), actual_(actual) {}
+
+ MessagePart(const MessagePart& other) = delete;
+ MessagePart& operator=(const MessagePart& other) = delete;
+
+ MessagePart(MessagePart&& other)
+ : data_(other.data_),
+ capacity_(other.capacity_),
+ actual_(other.actual_) {
+ other.data_ = nullptr;
+ other.capacity_ = 0u;
+ other.actual_ = 0u;
+ }
+
+ MessagePart& operator=(MessagePart&& other) {
+ if (this == &other)
+ return *this;
+ data_ = other.data_;
+ capacity_ = other.capacity_;
+ actual_ = other.actual_;
+ other.data_ = nullptr;
+ other.capacity_ = 0u;
+ other.actual_ = 0u;
+ return *this;
+ }
+
+ // The data stored in this part of the message.
+ T* data() const { return data_; }
+
+ // The total amount of storage available for this part of the message.
+ //
+ // This part of the message might not actually use all of this storage. To
+ // determine how much storage is actually being used, see |actual()|.
+ uint32_t capacity() const { return capacity_; }
+
+ // The amount of storage that is actually being used for this part of the
+ // message.
+ //
+ // There might be more storage available than is actually being used. To
+ // determine how much storage is available, see |capacity()|.
+ uint32_t actual() const { return actual_; }
+ void set_actual(uint32_t actual) { actual_ = actual; }
+
+ T* begin() { return data_; }
+ const T* begin() const { return data_; }
+ const T* cbegin() const { return data_; }
+
+ T* end() { return data_ + actual_; }
+ const T* end() const { return data_ + actual_; }
+ const T* cend() const { return data_ + actual_; }
+
+ size_t size() const { return actual_; }
+
+private:
+ T* data_;
+ uint32_t capacity_;
+ uint32_t actual_;
+};
+
+using BytePart = MessagePart<uint8_t>;
+using HandlePart = MessagePart<zx_handle_t>;
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_MESSAGE_PART_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/string_view.h b/pkg/fidl/include/lib/fidl/cpp/string_view.h
new file mode 100644
index 0000000..88eec42
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/string_view.h
@@ -0,0 +1,46 @@
+// Copyright 2017 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.
+
+#ifndef LIB_FIDL_CPP_STRING_VIEW_H_
+#define LIB_FIDL_CPP_STRING_VIEW_H_
+
+#include <zircon/fidl.h>
+
+namespace fidl {
+
+class StringView : public fidl_string_t {
+public:
+ StringView() : fidl_string_t{} {}
+
+ uint64_t size() const { return fidl_string_t::size; }
+ void set_size(uint64_t size) { fidl_string_t::size = size; }
+
+ const char* data() const { return fidl_string_t::data; }
+ void set_data(char* data) { fidl_string_t::data = data; }
+
+ char* mutable_data() const { return fidl_string_t::data; }
+
+ bool is_null() const { return fidl_string_t::data == nullptr; }
+ bool empty() const { return fidl_string_t::size == 0; }
+
+ const char& at(size_t offset) const { return data()[offset]; }
+ char& at(size_t offset) { return mutable_data()[offset]; }
+
+ const char& operator[](size_t offset) const { return at(offset); }
+ char& operator[](size_t offset) { return at(offset); }
+
+ char* begin() { return mutable_data(); }
+ const char* begin() const { return data(); }
+ const char* cbegin() const { return data(); }
+
+ char* end() { return mutable_data() + size(); }
+ const char* end() const { return data() + size(); }
+ const char* cend() const { return data() + size(); }
+
+ fidl_string_t* impl() { return this; }
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_STRING_VIEW_H_
diff --git a/pkg/fidl/include/lib/fidl/cpp/vector_view.h b/pkg/fidl/include/lib/fidl/cpp/vector_view.h
new file mode 100644
index 0000000..df4cedf
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/cpp/vector_view.h
@@ -0,0 +1,47 @@
+// Copyright 2017 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.
+
+#ifndef LIB_FIDL_CPP_VECTOR_VIEW_H_
+#define LIB_FIDL_CPP_VECTOR_VIEW_H_
+
+#include <zircon/fidl.h>
+
+namespace fidl {
+
+template<typename T>
+class VectorView : public fidl_vector_t {
+public:
+ VectorView() : fidl_vector_t{} {}
+
+ uint64_t count() const { return fidl_vector_t::count; }
+ void set_count(uint64_t count) { fidl_vector_t::count = count; }
+
+ const T* data() const { return static_cast<T*>(fidl_vector_t::data); }
+ void set_data(T* data) { fidl_vector_t::data = data; }
+
+ T* mutable_data() const { return static_cast<T*>(fidl_vector_t::data); }
+
+ bool is_null() const { return fidl_vector_t::data == nullptr; }
+ bool empty() const { return fidl_vector_t::count == 0; }
+
+ const T& at(size_t offset) const { return data()[offset]; }
+ T& at(size_t offset) { return mutable_data()[offset]; }
+
+ const T& operator[](size_t offset) const { return at(offset); }
+ T& operator[](size_t offset) { return at(offset); }
+
+ T* begin() { return mutable_data(); }
+ const T* begin() const { return data(); }
+ const T* cbegin() const { return data(); }
+
+ T* end() { return mutable_data() + count(); }
+ const T* end() const { return data() + count(); }
+ const T* cend() const { return data() + count(); }
+
+ fidl_vector_t* impl() { return this; }
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_VECTOR_VIEW_H_
diff --git a/pkg/fidl/include/lib/fidl/epitaph.h b/pkg/fidl/include/lib/fidl/epitaph.h
new file mode 100644
index 0000000..55653a7
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/epitaph.h
@@ -0,0 +1,22 @@
+// 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.
+
+#ifndef LIB_FIDL_EPITAPH_H_
+#define LIB_FIDL_EPITAPH_H_
+
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+#ifdef __Fuchsia__
+
+// Sends an epitaph with the given values down the channel.
+// See https://fuchsia.googlesource.com/docs/+/master/development/languages/fidl/languages/c.md#fidl_epitaph_write
+zx_status_t fidl_epitaph_write(zx_handle_t channel, zx_status_t error);
+
+#endif // __Fuchsia__
+
+__END_CDECLS
+
+#endif // LIB_FIDL_EPITAPH_H_
diff --git a/pkg/fidl/include/lib/fidl/internal.h b/pkg/fidl/include/lib/fidl/internal.h
new file mode 100644
index 0000000..efef16e
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/internal.h
@@ -0,0 +1,242 @@
+// Copyright 2017 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.
+
+#ifndef LIB_FIDL_INTERNAL_H_
+#define LIB_FIDL_INTERNAL_H_
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <lib/fidl/coding.h>
+#include <zircon/syscalls/object.h>
+#include <zircon/types.h>
+
+// All sizes here are given as uint32_t. Fidl message sizes are bounded to well below UINT32_MAX.
+// This also applies to arrays and vectors. For arrays, element_count * element_size will always fit
+// with 32 bits. For vectors, max_count * element_size will always fit within 32 bits.
+
+// Pointers to other type tables within a type are always nonnull, with the exception of vectors.
+// In that case, a null pointer indicates that the element type of the vector has no interesting
+// information to be decoded (i.e. no pointers or handles). The vector type still needs to be
+// emitted as it contains the information about the size of its secondary object. Contrast this with
+// arrays: being inline, ones with no interesting coding information can be elided, just like a
+// uint32 field in a struct is elided.
+
+namespace fidl {
+
+enum FidlNullability : uint32_t {
+ kNonnullable = 0u,
+ kNullable = 1u,
+};
+
+inline uint64_t FidlAlign(uint32_t offset) {
+ constexpr uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
+ return (offset + alignment_mask) & ~alignment_mask;
+}
+
+struct FidlField {
+ const fidl_type* type;
+ uint32_t offset;
+
+ constexpr FidlField(const fidl_type* type, uint32_t offset)
+ : type(type), offset(offset) {}
+};
+
+struct FidlTableField {
+ const fidl_type* type;
+ uint32_t ordinal;
+
+ constexpr FidlTableField(const fidl_type* type, uint32_t ordinal)
+ : type(type), ordinal(ordinal) {}
+};
+
+enum FidlTypeTag : uint32_t {
+ kFidlTypeStruct = 0u,
+ kFidlTypeStructPointer = 1u,
+ kFidlTypeTable = 8u,
+ kFidlTypeTablePointer = 9u,
+ kFidlTypeUnion = 2u,
+ kFidlTypeUnionPointer = 3u,
+ kFidlTypeArray = 4u,
+ kFidlTypeString = 5u,
+ kFidlTypeHandle = 6u,
+ kFidlTypeVector = 7u,
+};
+
+// Though the |size| is implied by the fields, computing that information is not the purview of this
+// library. It's easier for the compiler to stash it.
+struct FidlCodedStruct {
+ const FidlField* const fields;
+ const uint32_t field_count;
+ const uint32_t size;
+ const char* name; // may be nullptr if omitted at compile time
+
+ constexpr FidlCodedStruct(const FidlField* fields, uint32_t field_count, uint32_t size,
+ const char* name)
+ : fields(fields), field_count(field_count), size(size), name(name) {}
+};
+
+struct FidlCodedStructPointer {
+ const FidlCodedStruct* const struct_type;
+
+ constexpr explicit FidlCodedStructPointer(const FidlCodedStruct* struct_type)
+ : struct_type(struct_type) {}
+};
+
+struct FidlCodedTable {
+ const FidlTableField* const fields;
+ const uint32_t field_count;
+ const char* name; // may be nullptr if omitted at compile time
+
+ constexpr FidlCodedTable(const FidlTableField* fields, uint32_t field_count,
+ const char* name)
+ : fields(fields), field_count(field_count), name(name) {}
+};
+
+struct FidlCodedTablePointer {
+ const FidlCodedTable* const table_type;
+
+ constexpr explicit FidlCodedTablePointer(const FidlCodedTable* table_type)
+ : table_type(table_type) {}
+};
+
+// Unlike structs, union members do not have different offsets, so this points to an array of
+// |fidl_type*| rather than |FidlField|.
+//
+// On-the-wire unions begin with a tag which is an index into |types|.
+// |data_offset| is the offset of the data in the wire format (tag + padding).
+struct FidlCodedUnion {
+ const fidl_type* const* types;
+ const uint32_t type_count;
+ const uint32_t data_offset;
+ const uint32_t size;
+ const char* name; // may be nullptr if omitted at compile time
+
+ constexpr FidlCodedUnion(const fidl_type* const* types, uint32_t type_count,
+ uint32_t data_offset, uint32_t size, const char* name)
+ : types(types), type_count(type_count), data_offset(data_offset), size(size), name(name) {}
+};
+
+struct FidlCodedUnionPointer {
+ const FidlCodedUnion* const union_type;
+
+ constexpr explicit FidlCodedUnionPointer(const FidlCodedUnion* union_type)
+ : union_type(union_type) {}
+};
+
+// An array is essentially a struct with |array_size / element_size| of the same field, named at
+// |element|.
+struct FidlCodedArray {
+ const fidl_type* const element;
+ const uint32_t array_size;
+ const uint32_t element_size;
+
+ constexpr FidlCodedArray(const fidl_type* element, uint32_t array_size, uint32_t element_size)
+ : element(element), array_size(array_size), element_size(element_size) {}
+};
+
+// Note: must keep in sync with fidlc types.h HandleSubtype.
+enum FidlHandleSubtype : zx_obj_type_t {
+ // special case to indicate subtype is not specified.
+ kFidlHandleSubtypeHandle = ZX_OBJ_TYPE_NONE,
+
+ kFidlHandleSubtypeProcess = ZX_OBJ_TYPE_PROCESS,
+ kFidlHandleSubtypeThread = ZX_OBJ_TYPE_THREAD,
+ kFidlHandleSubtypeVmo = ZX_OBJ_TYPE_VMO,
+ kFidlHandleSubtypeChannel = ZX_OBJ_TYPE_CHANNEL,
+ kFidlHandleSubtypeEvent = ZX_OBJ_TYPE_EVENT,
+ kFidlHandleSubtypePort = ZX_OBJ_TYPE_PORT,
+ kFidlHandleSubtypeInterrupt = ZX_OBJ_TYPE_INTERRUPT,
+ kFidlHandleSubtypeLog = ZX_OBJ_TYPE_LOG,
+ kFidlHandleSubtypeSocket = ZX_OBJ_TYPE_SOCKET,
+ kFidlHandleSubtypeResource = ZX_OBJ_TYPE_RESOURCE,
+ kFidlHandleSubtypeEventpair = ZX_OBJ_TYPE_EVENTPAIR,
+ kFidlHandleSubtypeJob = ZX_OBJ_TYPE_JOB,
+ kFidlHandleSubtypeVmar = ZX_OBJ_TYPE_VMAR,
+ kFidlHandleSubtypeFifo = ZX_OBJ_TYPE_FIFO,
+ kFidlHandleSubtypeGuest = ZX_OBJ_TYPE_GUEST,
+ kFidlHandleSubtypeTimer = ZX_OBJ_TYPE_TIMER,
+};
+
+struct FidlCodedHandle {
+ const zx_obj_type_t handle_subtype;
+ const FidlNullability nullable;
+
+ constexpr FidlCodedHandle(uint32_t handle_subtype, FidlNullability nullable)
+ : handle_subtype(handle_subtype), nullable(nullable) {}
+
+ static_assert(ZX_OBJ_TYPE_LAST <= UINT32_MAX, "");
+};
+
+struct FidlCodedString {
+ const uint32_t max_size;
+ const FidlNullability nullable;
+
+ constexpr FidlCodedString(uint32_t max_size, FidlNullability nullable)
+ : max_size(max_size), nullable(nullable) {}
+};
+
+// Note that |max_count * element_size| is guaranteed to fit into a uint32_t. Unlike other types,
+// the |element| pointer may be null. This occurs when the element type contains no interesting bits
+// (i.e. pointers or handles).
+struct FidlCodedVector {
+ const fidl_type* const element;
+ const uint32_t max_count;
+ const uint32_t element_size;
+ const FidlNullability nullable;
+
+ constexpr FidlCodedVector(const fidl_type* element, uint32_t max_count, uint32_t element_size,
+ FidlNullability nullable)
+ : element(element), max_count(max_count), element_size(element_size), nullable(nullable) {}
+};
+
+} // namespace fidl
+
+struct fidl_type {
+ const fidl::FidlTypeTag type_tag;
+ const union {
+ const fidl::FidlCodedStruct coded_struct;
+ const fidl::FidlCodedStructPointer coded_struct_pointer;
+ const fidl::FidlCodedTable coded_table;
+ const fidl::FidlCodedTablePointer coded_table_pointer;
+ const fidl::FidlCodedUnion coded_union;
+ const fidl::FidlCodedUnionPointer coded_union_pointer;
+ const fidl::FidlCodedHandle coded_handle;
+ const fidl::FidlCodedString coded_string;
+ const fidl::FidlCodedArray coded_array;
+ const fidl::FidlCodedVector coded_vector;
+ };
+
+ constexpr fidl_type(fidl::FidlCodedStruct coded_struct)
+ : type_tag(fidl::kFidlTypeStruct), coded_struct(coded_struct) {}
+
+ constexpr fidl_type(fidl::FidlCodedStructPointer coded_struct_pointer)
+ : type_tag(fidl::kFidlTypeStructPointer), coded_struct_pointer(coded_struct_pointer) {}
+
+ constexpr fidl_type(fidl::FidlCodedTable coded_table)
+ : type_tag(fidl::kFidlTypeTable), coded_table(coded_table) {}
+
+ constexpr fidl_type(fidl::FidlCodedTablePointer coded_table_pointer)
+ : type_tag(fidl::kFidlTypeTablePointer), coded_table_pointer(coded_table_pointer) {}
+
+ constexpr fidl_type(fidl::FidlCodedUnion coded_union)
+ : type_tag(fidl::kFidlTypeUnion), coded_union(coded_union) {}
+
+ constexpr fidl_type(fidl::FidlCodedUnionPointer coded_union_pointer)
+ : type_tag(fidl::kFidlTypeUnionPointer), coded_union_pointer(coded_union_pointer) {}
+
+ constexpr fidl_type(fidl::FidlCodedHandle coded_handle)
+ : type_tag(fidl::kFidlTypeHandle), coded_handle(coded_handle) {}
+
+ constexpr fidl_type(fidl::FidlCodedString coded_string)
+ : type_tag(fidl::kFidlTypeString), coded_string(coded_string) {}
+
+ constexpr fidl_type(fidl::FidlCodedArray coded_array)
+ : type_tag(fidl::kFidlTypeArray), coded_array(coded_array) {}
+
+ constexpr fidl_type(fidl::FidlCodedVector coded_vector)
+ : type_tag(fidl::kFidlTypeVector), coded_vector(coded_vector) {}
+};
+
+#endif // LIB_FIDL_INTERNAL_H_
diff --git a/pkg/fidl/include/lib/fidl/llcpp/decoded_message.h b/pkg/fidl/include/lib/fidl/llcpp/decoded_message.h
new file mode 100644
index 0000000..52d0ec5
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/llcpp/decoded_message.h
@@ -0,0 +1,119 @@
+// 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.
+
+#ifndef LIB_FIDL_LLCPP_DECODED_MESSAGE_H_
+#define LIB_FIDL_LLCPP_DECODED_MESSAGE_H_
+
+#include <lib/fidl/coding.h>
+#include <lib/fidl/llcpp/encoded_message.h>
+#include <lib/fidl/llcpp/traits.h>
+#include <type_traits>
+#include <zircon/fidl.h>
+
+namespace fidl {
+
+// `DecodedMessage` manages a linearized FIDL message in decoded form.
+// It takes care of releasing all handles which were not consumed
+// (std::moved from the decoded FIDL struct) when it goes out of scope.
+template <typename FidlType>
+class DecodedMessage final {
+ static_assert(IsFidlType<FidlType>::value, "Only FIDL types allowed here");
+ static_assert(FidlType::MaxSize > 0, "Positive message size");
+
+public:
+ // Instantiates an empty message.
+ // To populate this message, decode from an EncodedMessage object.
+ DecodedMessage() = default;
+
+ // Instantiates a DecodedMessage which points to a buffer region with caller-managed memory.
+ // The buffer region is assumed to contain a linearized FIDL message with valid pointers.
+ // This does not take ownership of that buffer region.
+ // But it does take ownership of the handles within the buffer.
+ DecodedMessage(BytePart bytes) :
+ bytes_(std::move(bytes)) { }
+
+ DecodedMessage(DecodedMessage&& other) = default;
+
+ DecodedMessage& operator=(DecodedMessage&& other) = default;
+
+ DecodedMessage(const DecodedMessage& other) = delete;
+
+ DecodedMessage& operator=(const DecodedMessage& other) = delete;
+
+ ~DecodedMessage() {
+ CloseHandles();
+ }
+
+ // Keeps track of a new buffer region with caller-managed memory.
+ // The buffer region is assumed to contain a linearized FIDL message with valid pointers.
+ // This does not take ownership of that buffer region.
+ // But it does take ownership of the handles within the buffer.
+ void Reset(BytePart bytes) {
+ CloseHandles();
+ bytes_ = std::move(bytes);
+ }
+
+ // Consumes an encoded message object containing FIDL encoded bytes and handles.
+ // The current buffer region in DecodedMessage is always released.
+ // Uses the FIDL encoding tables to deserialize the message in-place.
+ // If the message is invalid, discards the buffer and returns an error.
+ zx_status_t DecodeFrom(EncodedMessage<FidlType>* msg, const char** out_error_msg) {
+ // Clear any existing message.
+ CloseHandles();
+ bytes_ = BytePart();
+ zx_status_t status = fidl_decode(FidlType::type,
+ msg->bytes().data(), msg->bytes().actual(),
+ msg->handles().data(), msg->handles().actual(),
+ out_error_msg);
+ // Clear out |msg| independent of success or failure
+ BytePart bytes = msg->ReleaseBytesAndHandles();
+ if (status == ZX_OK) {
+ Reset(std::move(bytes));
+ } else {
+ Reset(BytePart());
+ }
+ return status;
+ }
+
+ // Serializes the content of the message in-place and stores the result
+ // in |out_msg|. The message's contents are always consumed by this
+ // operation, even in case of an error.
+ zx_status_t EncodeTo(EncodedMessage<FidlType>* out_msg, const char** out_error_msg) {
+ return out_msg->Initialize([this, &out_error_msg] (BytePart& msg_bytes,
+ HandlePart& msg_handles) {
+ msg_bytes = std::move(bytes_);
+ uint32_t actual_handles = 0;
+ zx_status_t status = fidl_encode(FidlType::type,
+ msg_bytes.data(), msg_bytes.actual(),
+ msg_handles.data(), msg_handles.capacity(),
+ &actual_handles, out_error_msg);
+ msg_handles.set_actual(actual_handles);
+ return status;
+ });
+ }
+
+ // Accesses the FIDL message by reinterpreting the buffer pointer.
+ // Returns nullptr if there is no message.
+ FidlType* message() const {
+ return reinterpret_cast<FidlType*>(bytes_.data());
+ }
+
+private:
+ // Use the FIDL encoding tables for |FidlType| to walk the message and
+ // destroy the handles it contains.
+ void CloseHandles() {
+#ifdef __Fuchsia__
+ if (bytes_.data()) {
+ fidl_close_handles(FidlType::type, bytes_.data(), bytes_.actual(), nullptr);
+ }
+#endif
+ }
+
+ // The contents of the decoded message.
+ BytePart bytes_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_LLCPP_DECODED_MESSAGE_H_
diff --git a/pkg/fidl/include/lib/fidl/llcpp/encoded_message.h b/pkg/fidl/include/lib/fidl/llcpp/encoded_message.h
new file mode 100644
index 0000000..8fd1a06
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/llcpp/encoded_message.h
@@ -0,0 +1,136 @@
+// 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.
+
+#ifndef LIB_FIDL_LLCPP_ENCODED_MESSAGE_H_
+#define LIB_FIDL_LLCPP_ENCODED_MESSAGE_H_
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <lib/fidl/cpp/message_part.h>
+#include <lib/fidl/llcpp/traits.h>
+#include <lib/fit/traits.h>
+#include <type_traits>
+#include <utility>
+#include <zircon/fidl.h>
+
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
+namespace fidl {
+
+// Holds an encoded FIDL message, that is, a byte array plus a handle table.
+//
+// The bytes part points to an external caller-managed buffer, while the handles part
+// is owned by this class. Any handles will be closed upon destruction.
+// This class is aware of the upper bound on the number of handles
+// in a message, such that its size can be adjusted to fit the demands
+// of a specific FIDL type.
+//
+// Because this class does not own the underlying message buffer, the caller
+// must make sure the lifetime of this class does not extend over that of the buffer.
+template <typename FidlType>
+class EncodedMessage final {
+ static_assert(IsFidlType<FidlType>::value, "Only FIDL types allowed here");
+ static_assert(FidlType::MaxSize > 0, "Positive message size");
+
+public:
+ // Instantiates an empty buffer with no bytes or handles.
+ EncodedMessage() = default;
+
+ EncodedMessage(EncodedMessage&& other) noexcept {
+ if (this != &other) {
+ MoveImpl(std::move(other));
+ }
+ }
+
+ EncodedMessage& operator=(EncodedMessage&& other) noexcept {
+ if (this != &other) {
+ MoveImpl(std::move(other));
+ }
+ return *this;
+ }
+
+ EncodedMessage(const EncodedMessage& other) = delete;
+
+ EncodedMessage& operator=(const EncodedMessage& other) = delete;
+
+ // Instantiates an EncodedMessage which points to a buffer region with caller-managed memory.
+ // It does not take ownership of that buffer region.
+ // Also initializes an empty handles part.
+ EncodedMessage(BytePart bytes) :
+ bytes_(std::move(bytes)) { }
+
+ ~EncodedMessage() {
+ CloseHandles();
+ }
+
+ // Takes ownership of the contents of the message.
+ // The bytes and handle parts will become empty, while the existing bytes part is returned.
+ // The caller is responsible for having transferred the handles elsewhere
+ // before calling this method.
+ BytePart ReleaseBytesAndHandles() {
+ handles_.set_actual(0);
+ BytePart return_bytes = std::move(bytes_);
+ return return_bytes;
+ }
+
+ const BytePart& bytes() const { return bytes_; }
+
+ const HandlePart& handles() const { return handles_; }
+
+ // Clears the contents of the EncodedMessage then invokes Callback
+ // to initialize the EncodedMessage in-place then returns the callback's
+ // result.
+ //
+ // |callback| is a callable object whose arguments are (BytePart&, HandlePart&).
+ template <typename Callback>
+ decltype(auto) Initialize(Callback callback) {
+ static_assert(
+ fit::callable_traits<Callback>::args::size == 2 &&
+ std::is_same<
+ BytePart&,
+ typename fit::template callable_traits<Callback>::args::template at<0>>::value &&
+ std::is_same<
+ HandlePart&,
+ typename fit::template callable_traits<Callback>::args::template at<1>>::value,
+ "Callback signature must be: T(BytePart&, HandlePart&).");
+ bytes_ = BytePart();
+ CloseHandles();
+ return callback(bytes_, handles_);
+ }
+
+private:
+ void CloseHandles() {
+ if (handles_.actual() > 0) {
+#ifdef __Fuchsia__
+ zx_handle_close_many(handles_.data(), handles_.actual());
+#else
+ // How did we have handles if not on Fuchsia? Something bad happened...
+ assert(false);
+#endif
+ handles_.set_actual(0);
+ }
+ }
+
+ void MoveImpl(EncodedMessage&& other) noexcept {
+ CloseHandles();
+ bytes_ = std::move(other.bytes_);
+ // copy handles from |other|
+ memcpy(handle_storage_, other.handle_storage_,
+ other.handles_.actual() * sizeof(zx_handle_t));
+ // release handles in |other|
+ handles_.set_actual(other.handles().actual());
+ other.handles_.set_actual(0);
+ }
+
+ BytePart bytes_;
+ zx_handle_t handle_storage_[FidlType::MaxNumHandles];
+ HandlePart handles_{handle_storage_, FidlType::MaxNumHandles};
+};
+
+}
+
+#endif // LIB_FIDL_LLCPP_ENCODED_MESSAGE_H_
diff --git a/pkg/fidl/include/lib/fidl/llcpp/traits.h b/pkg/fidl/include/lib/fidl/llcpp/traits.h
new file mode 100644
index 0000000..80fadd7
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/llcpp/traits.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef LIB_FIDL_LLCPP_TRAITS_H_
+#define LIB_FIDL_LLCPP_TRAITS_H_
+
+#include <lib/zx/object.h>
+#include <stdint.h>
+#include <type_traits>
+#include <zircon/fidl.h>
+
+// Defines type traits used in the low-level C++ binding.
+//
+// The contracts of a FIDL type |T| are as follows:
+//
+// |IsFidlType<T>| resolves to std::true_type.
+// |IsFidlMessage<T>| resolves to std::true_type iff |T| is a transactional message.
+// |T::MaxNumHandles| is an uint32_t specifying the upper bound on the number of contained handles.
+// |T::MaxSize| is an uint32_t specifying the upper bound on the message byte size.
+// It is std::numeric_limits<uint32_t>::max() if |T| is unbounded.
+// |T::type| is a fidl_type_t* pointing to the corresponding encoding table, if any.
+//
+
+namespace fidl {
+
+// A type trait that indicates whether the given type is a request/response type
+// i.e. has a FIDL message header.
+template <typename T> struct IsFidlMessage : public std::false_type {};
+
+// Code-gen will explicitly conform the generated FIDL transactional messages to IsFidlMessage.
+
+// A type trait that indicates whether the given type is allowed to appear in
+// generated binding APIs and can be encoded/decoded.
+// As a start, all handle types are supported.
+template <typename T> struct IsFidlType :
+ public std::is_base_of<zx::object_base, T> {};
+
+// clang-format off
+// Specialize for primitives
+template <> struct IsFidlType<bool> : public std::true_type {};
+template <> struct IsFidlType<uint8_t> : public std::true_type {};
+template <> struct IsFidlType<uint16_t> : public std::true_type {};
+template <> struct IsFidlType<uint32_t> : public std::true_type {};
+template <> struct IsFidlType<uint64_t> : public std::true_type {};
+template <> struct IsFidlType<int8_t> : public std::true_type {};
+template <> struct IsFidlType<int16_t> : public std::true_type {};
+template <> struct IsFidlType<int32_t> : public std::true_type {};
+template <> struct IsFidlType<int64_t> : public std::true_type {};
+template <> struct IsFidlType<float> : public std::true_type {};
+template <> struct IsFidlType<double> : public std::true_type {};
+// clang-format on
+
+// String
+class StringView;
+template <> struct IsFidlType<StringView> : public std::true_type {};
+
+// Vector (conditional on element)
+template <typename E> class VectorView;
+template <typename E>
+struct IsFidlType<VectorView<E>> : public IsFidlType<E> {};
+
+// Code-gen will explicitly conform the generated FIDL structures to IsFidlType.
+
+} // namespace fidl
+
+#endif // LIB_FIDL_LLCPP_TRAITS_H_
diff --git a/pkg/fidl/include/lib/fidl/transport.h b/pkg/fidl/include/lib/fidl/transport.h
new file mode 100644
index 0000000..2da0d9c
--- /dev/null
+++ b/pkg/fidl/include/lib/fidl/transport.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef LIB_FIDL_TRANSPORT_H_
+#define LIB_FIDL_TRANSPORT_H_
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+// Writes |capacity| bytes from |buffer| to the control channel of |socket|.
+//
+// Blocks until |socket| is able to accept a control plane message.
+zx_status_t fidl_socket_write_control(zx_handle_t socket, const void* buffer,
+ size_t capacity);
+
+// Reads |capacity| bytes from the control channel of |socket| to |buffer|.
+//
+// Blocks until a control plane message is able to be read from |socket|.
+//
+// The actual number of bytes reads from the control plan is returned in
+// |out_actual|.
+zx_status_t fidl_socket_read_control(zx_handle_t socket, void* buffer,
+ size_t capacity, size_t* out_actual);
+
+// Issues a transaction on the control channel of |socket|.
+//
+// First, writes |capacity| bytes from |buffer| to the control channel of
+// |socket|. Second, reads |out_capacity| bytes from the control channel of
+// |socket| to |out_buffer|.
+//
+// Blocks until the transaction is complete.
+//
+// |buffer| and |out_buffer| may be aliased.
+//
+// The actual number of bytes reads from the control plan is returned in
+// |out_actual|.
+zx_status_t fidl_socket_call_control(zx_handle_t socket, const void* buffer,
+ size_t capacity, void* out_buffer,
+ size_t out_capacity, size_t* out_actual);
+
+__END_CDECLS
+
+#endif // LIB_FIDL_TRANSPORT_H_
diff --git a/pkg/fidl/message.cpp b/pkg/fidl/message.cpp
new file mode 100644
index 0000000..6857459
--- /dev/null
+++ b/pkg/fidl/message.cpp
@@ -0,0 +1,117 @@
+// 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/fidl/cpp/message.h>
+
+#include <string.h>
+
+#include <lib/fidl/coding.h>
+#include <lib/fidl/cpp/builder.h>
+
+#ifdef __Fuchsia__
+#include <zircon/syscalls.h>
+#endif
+
+namespace fidl {
+
+Message::Message() = default;
+
+Message::Message(BytePart bytes, HandlePart handles)
+ : bytes_(static_cast<BytePart&&>(bytes)),
+ handles_(static_cast<HandlePart&&>(handles)) {}
+
+Message::~Message() {
+#ifdef __Fuchsia__
+ zx_handle_close_many(handles_.data(), handles_.actual());
+#endif
+ ClearHandlesUnsafe();
+}
+
+Message::Message(Message&& other)
+ : bytes_(static_cast<BytePart&&>(other.bytes_)),
+ handles_(static_cast<HandlePart&&>(other.handles_)) {}
+
+Message& Message::operator=(Message&& other) {
+ bytes_ = static_cast<BytePart&&>(other.bytes_);
+ handles_ = static_cast<HandlePart&&>(other.handles_);
+ return *this;
+}
+
+zx_status_t Message::Encode(const fidl_type_t* type,
+ const char** error_msg_out) {
+ uint32_t actual_handles = 0u;
+ zx_status_t status = fidl_encode(type, bytes_.data(), bytes_.actual(),
+ handles_.data(), handles_.capacity(),
+ &actual_handles, error_msg_out);
+ if (status == ZX_OK)
+ handles_.set_actual(actual_handles);
+ return status;
+}
+
+zx_status_t Message::Decode(const fidl_type_t* type,
+ const char** error_msg_out) {
+ zx_status_t status = fidl_decode(type, bytes_.data(), bytes_.actual(),
+ handles_.data(), handles_.actual(),
+ error_msg_out);
+ ClearHandlesUnsafe();
+ return status;
+}
+
+zx_status_t Message::Validate(const fidl_type_t* type,
+ const char** error_msg_out) const {
+ return fidl_validate(type, bytes_.data(), bytes_.actual(),
+ handles_.actual(), error_msg_out);
+}
+
+#ifdef __Fuchsia__
+zx_status_t Message::Read(zx_handle_t channel, uint32_t flags) {
+ uint32_t actual_bytes = 0u;
+ uint32_t actual_handles = 0u;
+ zx_status_t status = zx_channel_read(
+ channel, flags, bytes_.data(), handles_.data(), bytes_.capacity(),
+ handles_.capacity(), &actual_bytes, &actual_handles);
+ if (status == ZX_OK) {
+ bytes_.set_actual(actual_bytes);
+ handles_.set_actual(actual_handles);
+ }
+ return status;
+}
+
+zx_status_t Message::Write(zx_handle_t channel, uint32_t flags) {
+ zx_status_t status = zx_channel_write(channel, flags, bytes_.data(),
+ bytes_.actual(), handles_.data(),
+ handles_.actual());
+ ClearHandlesUnsafe();
+ return status;
+}
+
+zx_status_t Message::Call(zx_handle_t channel, uint32_t flags,
+ zx_time_t deadline, Message* response) {
+ zx_channel_call_args_t args;
+ args.wr_bytes = bytes_.data();
+ args.wr_handles = handles_.data();
+ args.rd_bytes = response->bytes_.data();
+ args.rd_handles = response->handles_.data();
+ args.wr_num_bytes = bytes_.actual();
+ args.wr_num_handles = handles_.actual();
+ args.rd_num_bytes = response->bytes_.capacity();
+ args.rd_num_handles = response->handles_.capacity();
+ uint32_t actual_bytes = 0u;
+ uint32_t actual_handles = 0u;
+ zx_status_t status = zx_channel_call(channel, flags, deadline, &args,
+ &actual_bytes, &actual_handles);
+ ClearHandlesUnsafe();
+ if (status == ZX_OK) {
+ response->bytes_.set_actual(actual_bytes);
+ response->handles_.set_actual(actual_handles);
+ }
+ return status;
+}
+#endif
+
+void Message::ClearHandlesUnsafe() {
+ handles_.set_actual(0u);
+}
+
+} // namespace fidl
diff --git a/pkg/fidl/message_buffer.cpp b/pkg/fidl/message_buffer.cpp
new file mode 100644
index 0000000..54e2346
--- /dev/null
+++ b/pkg/fidl/message_buffer.cpp
@@ -0,0 +1,50 @@
+// 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/fidl/cpp/message_buffer.h>
+#include <zircon/assert.h>
+
+#include <stdlib.h>
+
+namespace fidl {
+namespace {
+
+uint64_t AddPadding(uint32_t offset) {
+ constexpr uint32_t kMask = alignof(zx_handle_t) - 1;
+ // Cast before addition to avoid overflow.
+ return static_cast<uint64_t>(offset) + static_cast<uint64_t>(offset & kMask);
+}
+
+size_t GetAllocSize(uint32_t bytes_capacity, uint32_t handles_capacity) {
+ return AddPadding(bytes_capacity) + sizeof(zx_handle_t) * handles_capacity;
+}
+
+} // namespace
+
+MessageBuffer::MessageBuffer(uint32_t bytes_capacity,
+ uint32_t handles_capacity)
+ : buffer_(static_cast<uint8_t*>(malloc(GetAllocSize(bytes_capacity, handles_capacity)))),
+ bytes_capacity_(bytes_capacity),
+ handles_capacity_(handles_capacity) {
+ ZX_ASSERT_MSG(buffer_, "malloc returned NULL in MessageBuffer::MessageBuffer()");
+}
+
+MessageBuffer::~MessageBuffer() {
+ free(buffer_);
+}
+
+zx_handle_t* MessageBuffer::handles() const {
+ return reinterpret_cast<zx_handle_t*>(buffer_ + AddPadding(bytes_capacity_));
+}
+
+Message MessageBuffer::CreateEmptyMessage() {
+ return Message(BytePart(bytes(), bytes_capacity()),
+ HandlePart(handles(), handles_capacity()));
+}
+
+Builder MessageBuffer::CreateBuilder() {
+ return Builder(bytes(), bytes_capacity());
+}
+
+} // namespace fidl
diff --git a/pkg/fidl/message_builder.cpp b/pkg/fidl/message_builder.cpp
new file mode 100644
index 0000000..c178690
--- /dev/null
+++ b/pkg/fidl/message_builder.cpp
@@ -0,0 +1,35 @@
+// 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/fidl/cpp/message_builder.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace fidl {
+
+MessageBuilder::MessageBuilder(const fidl_type_t* type,
+ uint32_t bytes_capacity,
+ uint32_t handles_capacity)
+ : type_(type),
+ buffer_(bytes_capacity, handles_capacity) {
+ Reset();
+}
+
+MessageBuilder::~MessageBuilder() = default;
+
+zx_status_t MessageBuilder::Encode(Message* message_out,
+ const char** error_msg_out) {
+ *message_out = Message(Finalize(),
+ HandlePart(buffer_.handles(),
+ buffer_.handles_capacity()));
+ return message_out->Encode(type_, error_msg_out);
+}
+
+void MessageBuilder::Reset() {
+ Builder::Reset(buffer_.bytes(), buffer_.bytes_capacity());
+ New<fidl_message_header_t>();
+}
+
+} // namespace fidl
diff --git a/pkg/fidl/meta.json b/pkg/fidl/meta.json
new file mode 100644
index 0000000..be54c7d
--- /dev/null
+++ b/pkg/fidl/meta.json
@@ -0,0 +1,67 @@
+{
+ "banjo_deps": [],
+ "deps": [],
+ "fidl_deps": [],
+ "files": [
+ "pkg/fidl/buffer_walker.h",
+ "pkg/fidl/builder.cpp",
+ "pkg/fidl/decoding.cpp",
+ "pkg/fidl/encoding.cpp",
+ "pkg/fidl/epitaph.c",
+ "pkg/fidl/formatting.cpp",
+ "pkg/fidl/handle_closing.cpp",
+ "pkg/fidl/message.cpp",
+ "pkg/fidl/message_buffer.cpp",
+ "pkg/fidl/message_builder.cpp",
+ "pkg/fidl/transport.cpp",
+ "pkg/fidl/validating.cpp",
+ "pkg/fidl/include/lib/fidl/coding.h",
+ "pkg/fidl/include/lib/fidl/cpp/builder.h",
+ "pkg/fidl/include/lib/fidl/cpp/message.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_buffer.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_builder.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_part.h",
+ "pkg/fidl/include/lib/fidl/cpp/string_view.h",
+ "pkg/fidl/include/lib/fidl/cpp/vector_view.h",
+ "pkg/fidl/include/lib/fidl/epitaph.h",
+ "pkg/fidl/include/lib/fidl/internal.h",
+ "pkg/fidl/include/lib/fidl/llcpp/decoded_message.h",
+ "pkg/fidl/include/lib/fidl/llcpp/encoded_message.h",
+ "pkg/fidl/include/lib/fidl/llcpp/traits.h",
+ "pkg/fidl/include/lib/fidl/transport.h"
+ ],
+ "headers": [
+ "pkg/fidl/include/lib/fidl/coding.h",
+ "pkg/fidl/include/lib/fidl/cpp/builder.h",
+ "pkg/fidl/include/lib/fidl/cpp/message.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_buffer.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_builder.h",
+ "pkg/fidl/include/lib/fidl/cpp/message_part.h",
+ "pkg/fidl/include/lib/fidl/cpp/string_view.h",
+ "pkg/fidl/include/lib/fidl/cpp/vector_view.h",
+ "pkg/fidl/include/lib/fidl/epitaph.h",
+ "pkg/fidl/include/lib/fidl/internal.h",
+ "pkg/fidl/include/lib/fidl/llcpp/decoded_message.h",
+ "pkg/fidl/include/lib/fidl/llcpp/encoded_message.h",
+ "pkg/fidl/include/lib/fidl/llcpp/traits.h",
+ "pkg/fidl/include/lib/fidl/transport.h"
+ ],
+ "include_dir": "pkg/fidl/include",
+ "name": "fidl",
+ "root": "pkg/fidl",
+ "sources": [
+ "pkg/fidl/buffer_walker.h",
+ "pkg/fidl/builder.cpp",
+ "pkg/fidl/decoding.cpp",
+ "pkg/fidl/encoding.cpp",
+ "pkg/fidl/epitaph.c",
+ "pkg/fidl/formatting.cpp",
+ "pkg/fidl/handle_closing.cpp",
+ "pkg/fidl/message.cpp",
+ "pkg/fidl/message_buffer.cpp",
+ "pkg/fidl/message_builder.cpp",
+ "pkg/fidl/transport.cpp",
+ "pkg/fidl/validating.cpp"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/fidl/transport.cpp b/pkg/fidl/transport.cpp
new file mode 100644
index 0000000..253b5ec
--- /dev/null
+++ b/pkg/fidl/transport.cpp
@@ -0,0 +1,70 @@
+// 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.
+
+#ifdef __Fuchsia__
+
+#include <lib/fidl/transport.h>
+#include <zircon/assert.h>
+#include <zircon/syscalls.h>
+
+zx_status_t fidl_socket_write_control(zx_handle_t socket, const void* buffer,
+ size_t capacity) {
+ for (;;) {
+ zx_status_t status = zx_socket_write(socket, ZX_SOCKET_CONTROL, buffer,
+ capacity, nullptr);
+ if (status != ZX_ERR_SHOULD_WAIT) {
+ return status;
+ }
+
+ zx_signals_t observed = ZX_SIGNAL_NONE;
+ status = zx_object_wait_one(socket, ZX_SOCKET_CONTROL_WRITABLE | ZX_SOCKET_PEER_CLOSED,
+ ZX_TIME_INFINITE, &observed);
+ if (status != ZX_OK) {
+ return status;
+ }
+
+ if (observed & ZX_SOCKET_PEER_CLOSED) {
+ return ZX_ERR_PEER_CLOSED;
+ }
+
+ ZX_ASSERT(observed & ZX_SOCKET_CONTROL_WRITABLE);
+ }
+}
+
+zx_status_t fidl_socket_read_control(zx_handle_t socket, void* buffer,
+ size_t capacity, size_t* out_actual) {
+ for (;;) {
+ zx_status_t status = zx_socket_read(socket, ZX_SOCKET_CONTROL, buffer,
+ capacity, out_actual);
+ if (status != ZX_ERR_SHOULD_WAIT) {
+ return status;
+ }
+
+ zx_signals_t observed = ZX_SIGNAL_NONE;
+ status = zx_object_wait_one(socket, ZX_SOCKET_CONTROL_READABLE | ZX_SOCKET_PEER_CLOSED,
+ ZX_TIME_INFINITE, &observed);
+ if (status != ZX_OK) {
+ return status;
+ }
+
+ if (observed & ZX_SOCKET_CONTROL_READABLE) {
+ continue;
+ }
+
+ ZX_ASSERT(observed & ZX_SOCKET_PEER_CLOSED);
+ return ZX_ERR_PEER_CLOSED;
+ }
+}
+
+zx_status_t fidl_socket_call_control(zx_handle_t socket, const void* buffer,
+ size_t capacity, void* out_buffer,
+ size_t out_capacity, size_t* out_actual) {
+ zx_status_t status = fidl_socket_write_control(socket, buffer, capacity);
+ if (status != ZX_OK) {
+ return status;
+ }
+ return fidl_socket_read_control(socket, out_buffer, out_capacity, out_actual);
+}
+
+#endif
diff --git a/pkg/fidl/validating.cpp b/pkg/fidl/validating.cpp
new file mode 100644
index 0000000..342face
--- /dev/null
+++ b/pkg/fidl/validating.cpp
@@ -0,0 +1,90 @@
+// Copyright 2017 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/fidl/coding.h>
+
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <lib/fidl/internal.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+#include "buffer_walker.h"
+
+// TODO(kulakowski) Design zx_status_t error values.
+
+namespace {
+
+class FidlValidator final : public fidl::internal::BufferWalker<FidlValidator, false, false> {
+ typedef fidl::internal::BufferWalker<FidlValidator, false, false> Super;
+
+public:
+ FidlValidator(const fidl_type_t* type, const void* bytes, uint32_t num_bytes,
+ uint32_t num_handles, const char** out_error_msg)
+ : Super(type), bytes_(static_cast<const uint8_t*>(bytes)), num_bytes_(num_bytes),
+ num_handles_(num_handles), out_error_msg_(out_error_msg) {}
+
+ void Walk() {
+ Super::Walk();
+ if (status_ == ZX_OK && handle_idx() != num_handles()) {
+ SetError("message did not contain the specified number of handles");
+ return;
+ }
+ }
+
+ const uint8_t* bytes() const { return bytes_; }
+ uint32_t num_bytes() const { return num_bytes_; }
+ uint32_t num_handles() const { return num_handles_; }
+
+ bool ValidateOutOfLineStorageClaim(const void* a, const void* b) {
+ return true;
+ }
+
+ void UnclaimedHandle(const zx_handle_t* out_handle) {}
+ void ClaimedHandle(const zx_handle_t* out_handle, uint32_t idx) {}
+
+ template <class T>
+ void UpdatePointer(const T* const* p, const T* v) {}
+
+ PointerState GetPointerState(const void* ptr) const {
+ return static_cast<PointerState>(*static_cast<const uintptr_t*>(ptr));
+ }
+ HandleState GetHandleState(zx_handle_t p) const {
+ return static_cast<HandleState>(p);
+ }
+
+ void SetError(const char* error_msg) {
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error_msg;
+ }
+ }
+
+ zx_status_t status() const { return status_; }
+
+private:
+ // Message state passed in to the constructor.
+ const uint8_t* const bytes_;
+ const uint32_t num_bytes_;
+ const uint32_t num_handles_;
+ const char** const out_error_msg_;
+ zx_status_t status_ = ZX_OK;
+};
+
+} // namespace
+
+zx_status_t fidl_validate(const fidl_type_t* type, const void* bytes, uint32_t num_bytes,
+ uint32_t num_handles, const char** out_error_msg) {
+ FidlValidator validator(type, bytes, num_bytes, num_handles, out_error_msg);
+ validator.Walk();
+ return validator.status();
+}
+
+zx_status_t fidl_validate_msg(const fidl_type_t* type, const fidl_msg_t* msg,
+ const char** out_error_msg) {
+ return fidl_validate(type, msg->bytes, msg->num_bytes, msg->num_handles,
+ out_error_msg);
+}
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/binding.h b/pkg/fidl_cpp/include/lib/fidl/cpp/binding.h
new file mode 100644
index 0000000..05c0637
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/binding.h
@@ -0,0 +1,239 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_BINDING_H_
+#define LIB_FIDL_CPP_BINDING_H_
+
+#include <lib/async/dispatcher.h>
+#include <lib/fit/function.h>
+#include <lib/zx/channel.h>
+#include <zircon/assert.h>
+
+#include <memory>
+#include <utility>
+
+#include "lib/fidl/cpp/interface_handle.h"
+#include "lib/fidl/cpp/interface_ptr.h"
+#include "lib/fidl/cpp/interface_request.h"
+#include "lib/fidl/cpp/internal/stub_controller.h"
+
+namespace fidl {
+
+// Binds the implementation of |Interface| to a channel.
+//
+// The |Binding| listens for incoming messages on the channel, decodes them, and
+// calls the appropriate method on the bound implementation. If the message
+// expects a reply, the |Binding| creates callbacks that encode and send
+// reply messages when called.
+//
+// When the |Binding| object is destroyed, the binding between the channel
+// and the interface is torn down and the channel is closed, leaving the
+// |Binding| in an unbound state.
+//
+// The implementation pointer type of the binding is also parameterized,
+// allowing the use of smart pointer types such as |std::unique_ptr| to
+// reference the implementation.
+//
+// Example:
+//
+// #include "foo.fidl.h"
+//
+// class FooImpl : public Foo {
+// public:
+// explicit FooImpl(InterfaceRequest<Foo> request)
+// : binding_(this, std::move(request)) {}
+//
+// // Foo implementation here.
+//
+// private:
+// Binding<Foo> binding_;
+// };
+//
+// After the |Binding| has been bound to an implementation, the implementation
+// will receive methods calls from the remote endpoint of the channel on the
+// async_t on which the |InterfaceRequest| was bound. By default this is the
+// thread on which the binding occurred.
+//
+// See also:
+//
+// * |InterfacePtr|, which is the client analog of a |Binding|.
+template <typename Interface, typename ImplPtr = Interface*>
+class Binding {
+ public:
+ // Constructs an incomplete binding that will use the implementation |impl|.
+ //
+ // The binding may be completed with a subsequent call to the |Bind| method.
+ // Does not take ownership of |impl|, which must outlive the binding.
+ explicit Binding(ImplPtr impl)
+ : impl_(std::forward<ImplPtr>(impl)), stub_(&*this->impl()) {
+ controller_.set_stub(&stub_);
+ stub_.set_controller(&controller_);
+ }
+
+ // Constructs a completed binding of |channel| to implementation |impl|.
+ //
+ // Does not take ownership of |impl|, which must outlive the binding.
+ //
+ // If the |Binding| cannot be bound to the given |channel| (e.g., because
+ // the |channel| lacks |ZX_RIGHT_WAIT|), the |Binding| will be constructed
+ // in an unbound state.
+ //
+ // Uses the given async_dispatcher_t (e.g., a message loop) in order to read
+ // messages from the channel and to monitor the channel for
+ // |ZX_CHANNEL_PEER_CLOSED|. If |dispatcher| is null, the current thread must
+ // have a default async_dispatcher_t.
+ Binding(ImplPtr impl, zx::channel channel,
+ async_dispatcher_t* dispatcher = nullptr)
+ : Binding(std::forward<ImplPtr>(impl)) {
+ Bind(std::move(channel), dispatcher);
+ }
+
+ // Constructs a completed binding of |impl| to the channel in |request|.
+ //
+ // Does not take ownership of |impl|, which must outlive the binding.
+ //
+ // If the |Binding| cannot be bound to the given |channel| (e.g., because
+ // the |channel| lacks |ZX_RIGHT_WAIT|), the |Binding| will be constructed
+ // in an unbound state.
+ //
+ // Uses the given async_dispatcher_t (e.g., a message loop) in order to read
+ // messages from the channel and to monitor the channel for
+ // |ZX_CHANNEL_PEER_CLOSED|. If |dispatcher| is null, the current thread must
+ // have a default async_dispatcher_t.
+ Binding(ImplPtr impl, InterfaceRequest<Interface> request,
+ async_dispatcher_t* dispatcher = nullptr)
+ : Binding(std::forward<ImplPtr>(impl)) {
+ Bind(request.TakeChannel(), dispatcher);
+ }
+
+ Binding(const Binding&) = delete;
+ Binding& operator=(const Binding&) = delete;
+
+ // Completes a binding by creating a new channel, binding one endpoint to
+ // the previously specified implementation and returning the other endpoint.
+ //
+ // If |NewBinding| fails to create the underlying channel, the returned
+ // |InterfaceHandle| will return false from |is_valid()|.
+ //
+ // Uses the given async_dispatcher_t (e.g., a message loop) in order to read
+ // messages from the channel and to monitor the channel for
+ // |ZX_CHANNEL_PEER_CLOSED|. If |dispatcher| is null, the current thread must
+ // have a default async_dispatcher_t.
+ InterfaceHandle<Interface> NewBinding(
+ async_dispatcher_t* dispatcher = nullptr) {
+ InterfaceHandle<Interface> client;
+ Bind(client.NewRequest().TakeChannel(), dispatcher);
+ return client;
+ }
+
+ // Binds the previously specified implementation to the given |channel|.
+ //
+ // If the |Binding| was prevously bound to another channel, that channel is
+ // closed.
+ //
+ // Uses the given async_dispatcher_t (e.g., a message loop) in order to read
+ // messages from the channel and to monitor the channel for
+ // |ZX_CHANNEL_PEER_CLOSED|. If |dispatcher| is null, the current thread must
+ // have a default async_dispatcher_t.
+ //
+ // Returns an error if the binding was not able to be created (e.g., because
+ // the |channel| lacks |ZX_RIGHT_WAIT|).
+ zx_status_t Bind(zx::channel channel,
+ async_dispatcher_t* dispatcher = nullptr) {
+ return controller_.reader().Bind(std::move(channel), dispatcher);
+ }
+
+ // Binds the previously specified implementation to the given
+ // |InterfaceRequest|.
+ //
+ // If the |Binding| was prevously bound to another channel, that channel is
+ // closed.
+ //
+ // Uses the given async_dispatcher_t (e.g., a message loop) in order to read
+ // messages from the channel and to monitor the channel for
+ // |ZX_CHANNEL_PEER_CLOSED|. If |dispatcher| is null, the current thread must
+ // have a default async_dispatcher_t.
+ //
+ // Returns an error if the binding was not able to be created (e.g., because
+ // the |channel| lacks |ZX_RIGHT_WAIT|).
+ zx_status_t Bind(InterfaceRequest<Interface> request,
+ async_dispatcher_t* dispatcher = nullptr) {
+ return Bind(request.TakeChannel(), dispatcher);
+ }
+
+ // Unbinds the underlying channel from this binding and returns it so it can
+ // be used in another context, such as on another thread or with a different
+ // implementation.
+ //
+ // After this function returns, the |Binding| is ready to be bound to another
+ // channel.
+ InterfaceRequest<Interface> Unbind() {
+ return InterfaceRequest<Interface>(controller_.reader().Unbind());
+ }
+
+ // Sends an Epitaph over the bound channel corresponding to the error passed
+ // as a parameter, closes the channel, and unbinds it. An Epitaph is the last
+ // message sent over a channel before a close operation; for the purposes of
+ // this function, it can be thought of as a return code. See the FIDL
+ // language spec for more information about Epitaphs.
+ //
+ // The return value can be any of the return values of zx_channel_write.
+ zx_status_t Close(zx_status_t epitaph_value) {
+ return controller_.reader().Close(epitaph_value);
+ }
+
+ // Blocks the calling thread until either a message arrives on the previously
+ // bound channel or an error occurs.
+ //
+ // Returns an error if waiting for the message, reading the message, or
+ // processing the message fails. If the error results in the channel being
+ // closed, the error handler will be called synchronously before this
+ // method returns.
+ //
+ // This method can be called only if this |Binding| is currently bound to a
+ // channel.
+ zx_status_t WaitForMessage() {
+ return controller_.reader().WaitAndDispatchOneMessageUntil(
+ zx::time::infinite());
+ }
+
+ // Sets an error handler that will be called if an error causes the underlying
+ // channel to be closed.
+ //
+ // If the error is being reported because an error occurred on the local side
+ // of the channel, the zx_status_t of that error will be passed as the
+ // parameter to the handler.
+ //
+ // If an Epitaph was present on the channel, its error value will be passed as
+ // the parameter. See the FIDL language specification for more detail on
+ // Epitaphs.
+ //
+ // For example, the error handler will be called if the remote side of the
+ // channel sends an invalid message. When the error handler is called, the
+ // |Binding| will no longer be bound to the channel.
+ void set_error_handler(fit::function<void(zx_status_t)> error_handler) {
+ controller_.reader().set_error_handler(std::move(error_handler));
+ }
+
+ // The implementation used by this |Binding| to process incoming messages.
+ const ImplPtr& impl() const { return impl_; }
+
+ // The interface for sending events back to the client.
+ typename Interface::EventSender_& events() { return stub_; }
+
+ // Whether this |Binding| is currently listening to a channel.
+ bool is_bound() const { return controller_.reader().is_bound(); }
+
+ // The underlying channel.
+ const zx::channel& channel() const { return controller_.reader().channel(); }
+
+ private:
+ const ImplPtr impl_;
+ typename Interface::Stub_ stub_;
+ internal::StubController controller_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_BINDING_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/binding_set.h b/pkg/fidl_cpp/include/lib/fidl/cpp/binding_set.h
new file mode 100644
index 0000000..c505344
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/binding_set.h
@@ -0,0 +1,156 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_BINDING_SET_H_
+#define LIB_FIDL_CPP_BINDING_SET_H_
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <lib/fit/function.h>
+
+#include "lib/fidl/cpp/binding.h"
+
+namespace fidl {
+
+// Manages a set of bindings to implementations owned by the bound channels.
+//
+// The implementation pointer type of the binding is also parameterized,
+// allowing the use of smart pointer types such as |std::unique_ptr<>| to
+// reference the implementation.
+//
+// See also:
+//
+// * |InterfacePtrSet|, which is the client analog of |BindingSet|.
+template <typename Interface, typename ImplPtr = Interface*>
+class BindingSet {
+ public:
+ using Binding = ::fidl::Binding<Interface, ImplPtr>;
+ using StorageType = std::vector<std::unique_ptr<Binding>>;
+
+ using iterator = typename StorageType::iterator;
+
+ BindingSet() = default;
+
+ BindingSet(const BindingSet&) = delete;
+ BindingSet& operator=(const BindingSet&) = delete;
+
+ // Adds a binding to the set.
+ //
+ // The given |ImplPtr| is bound to the channel underlying the
+ // |InterfaceRequest|. The binding is removed (and the |~ImplPtr| called)
+ // when the created binding has an error (e.g., if the remote endpoint of
+ // the channel sends an invalid message).
+ //
+ // Whether this method takes ownership of |impl| depends on |ImplPtr|. If
+ // |ImplPtr| is a raw pointer, then this method does not take ownership of
+ // |impl|. If |ImplPtr| is a |unique_ptr|, then running |~ImplPtr| when the
+ // binding generates an error will delete |impl| because |~ImplPtr| is
+ // |~unique_ptr|, which deletes |impl|.
+ void AddBinding(ImplPtr impl, InterfaceRequest<Interface> request,
+ async_dispatcher_t* dispatcher = nullptr) {
+ bindings_.push_back(std::make_unique<Binding>(
+ std::forward<ImplPtr>(impl), std::move(request), dispatcher));
+ auto* binding = bindings_.back().get();
+ // Set the connection error handler for the newly added Binding to be a
+ // function that will erase it from the vector.
+ binding->set_error_handler([binding, this](zx_status_t status) {
+ this->RemoveOnError(binding);
+ });
+ }
+
+ // Adds a binding to the set for the given implementation.
+ //
+ // Creates a channel for the binding and returns the client endpoint of
+ // the channel as an |InterfaceHandle|. If |AddBinding| fails to create the
+ // underlying channel, the returned |InterfaceHandle| will return false from
+ // |is_valid()|.
+ //
+ // The given |ImplPtr| is bound to the newly created channel. The binding is
+ // removed (and the |~ImplPtr| called) when the created binding has an error
+ // (e.g., if the remote endpoint of the channel sends an invalid message).
+ //
+ // Whether this method takes ownership of |impl| depends on |ImplPtr|. If
+ // |ImplPtr| is a raw pointer, then this method does not take ownership of
+ // |impl|. If |ImplPtr| is a |unique_ptr|, then running |~ImplPtr| when the
+ // binding generates an error will delete |impl| because |~ImplPtr| is
+ // |~unique_ptr|, which deletes |impl|.
+ InterfaceHandle<Interface> AddBinding(
+ ImplPtr impl, async_dispatcher_t* dispatcher = nullptr) {
+ InterfaceHandle<Interface> handle;
+ InterfaceRequest<Interface> request = handle.NewRequest();
+ if (!request)
+ return nullptr;
+ AddBinding(std::forward<ImplPtr>(impl), std::move(request), dispatcher);
+ return handle;
+ }
+
+ // Returns an InterfaceRequestHandler that binds the incoming
+ // InterfaceRequests this object.
+ InterfaceRequestHandler<Interface> GetHandler(
+ ImplPtr impl, async_dispatcher_t* dispatcher = nullptr) {
+ return [this, impl, dispatcher](InterfaceRequest<Interface> request) {
+ AddBinding(impl, std::move(request), dispatcher);
+ };
+ }
+
+ // Removes all the bindings from the set.
+ //
+ // Closes all the channels associated with this |BindingSet|.
+ // Bindings are destroyed AFTER it is removed from the bindings set. An
+ // example of when this is useful is if an error handler on a binding has
+ // some behavior where it needs to read from the binding set; the set would
+ // then properly reflect that the binding is not present in the set.
+ void CloseAll() {
+ auto bindings_local = std::move(bindings_);
+ bindings_.clear();
+ }
+
+ // The number of bindings in this |BindingSet|.
+ size_t size() const { return bindings_.size(); }
+
+ // Called when the last binding has been removed from this |BindingSet|.
+ //
+ // This function is not called by |CloseAll| or by |~BindingSet|.
+ void set_empty_set_handler(fit::closure empty_set_handler) {
+ empty_set_handler_ = std::move(empty_set_handler);
+ }
+
+ // The bindings stored in this set.
+ //
+ // This collection of bindings can be invalidated when a |Binding| in the
+ // set encounters a connection error because connection errors causes the
+ // |BindingSet| to remove the |Binding| from the set.
+ const StorageType& bindings() const { return bindings_; }
+
+ private:
+ // Called when a binding has an error to remove the binding from the set.
+ void RemoveOnError(Binding* binding) {
+ auto it = std::find_if(bindings_.begin(), bindings_.end(),
+ [binding](const std::unique_ptr<Binding>& b) {
+ return b.get() == binding;
+ });
+ ZX_DEBUG_ASSERT(it != bindings_.end());
+
+ {
+ // Move ownership of binding out of storage, such that the binding is
+ // destroyed AFTER it is removed from the bindings.
+ auto binding_local = std::move(*it);
+ binding_local->set_error_handler(nullptr);
+ bindings_.erase(it);
+ }
+
+ if (bindings_.empty() && empty_set_handler_)
+ empty_set_handler_();
+ }
+
+ StorageType bindings_;
+ fit::closure empty_set_handler_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_BINDING_SET_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/enum.h b/pkg/fidl_cpp/include/lib/fidl/cpp/enum.h
new file mode 100644
index 0000000..19ec1eb
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/enum.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_ENUM_H_
+#define LIB_FIDL_CPP_ENUM_H_
+
+#include <type_traits>
+
+namespace fidl {
+
+// Converts an enum value to its underlying type.
+template <typename TEnum>
+constexpr auto ToUnderlying(TEnum value) ->
+ typename std::underlying_type<TEnum>::type {
+ return static_cast<typename std::underlying_type<TEnum>::type>(value);
+}
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_ENUM_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr.h b/pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr.h
new file mode 100644
index 0000000..e1af79a
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr.h
@@ -0,0 +1,305 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERFACE_PTR_H_
+#define LIB_FIDL_CPP_INTERFACE_PTR_H_
+
+#include <algorithm>
+#include <cstddef>
+#include <utility>
+
+#include <lib/fit/function.h>
+#include <lib/zx/channel.h>
+#include <zircon/assert.h>
+
+#include "lib/fidl/cpp/interface_handle.h"
+#include "lib/fidl/cpp/interface_request.h"
+#include "lib/fidl/cpp/internal/proxy_controller.h"
+
+namespace fidl {
+
+// A client interface to a remote implementation of |Interface|.
+//
+// An |InterfacePtr| implements |Interface| by proxying calls through a
+// |channel| to a remote implementation of |Interface|. Method calls on the
+// |Interface| proxy are encoded and sent through the bound channel to the
+// remote endpoint, which processes them. If the remote endpoint has not yet
+// been bound to an implementation, messages sent on the channel are buffered
+// by the channel, allowing for *pipelined* operation.
+//
+// The |InterfacePtr| also keeps state about the connection and about
+// outstanding request transactions that are expecting replies. When a the
+// |InterfacePtr| receives a reply to an outstanding transaction, the
+// |InterfacePtr| decodes the reply and calls the appropriate callback on the
+// thread on which the |InterfacePtr| was bound.
+//
+// You need to bind the |InterfacePtr| before calling any |Interface| methods.
+// There are a number of ways to bind the |InterfacePtr|. See |NewRequest|,
+// |Bind|, and the |Bind| method on |InterfaceHandle|.
+//
+// If the underlying channel experiences an error, the |InterfacePtr| will
+// unbind from the channel and call its error handler.
+//
+// This class is thread-hostile, as is the local proxy it manages. All calls to
+// this class or the proxy should be from the thread on which the
+// |InterfacePtr| was bound. If you need to move the proxy to a different
+// thread, extract the |InterfaceHandle| by calling |Unbind|, and pass the
+// |InterfaceHandle| to a different thread, which the |InterfaceHandle| can be
+// bound to an |InterfacePtr| again. This operation destroys the state about
+// outstanding request transactions that are expecting replies.
+//
+// See also:
+//
+// * |Binding|, which is the server analog of an |InterfacePtr|.
+// * |SynchronousInterfacePtr|, which is a synchronous client interface to a
+// remote implementation.
+template <typename Interface>
+class InterfacePtr {
+ public:
+ using Proxy = typename Interface::Proxy_;
+
+ // Creates an unbound |InterfacePtr|.
+ InterfacePtr() : impl_(new Impl) {}
+ InterfacePtr(std::nullptr_t) : InterfacePtr() {}
+
+ InterfacePtr(const InterfacePtr& other) = delete;
+ InterfacePtr& operator=(const InterfacePtr& other) = delete;
+
+ InterfacePtr(InterfacePtr&& other) : impl_(std::move(other.impl_)) {
+ other.impl_.reset(new Impl);
+ }
+
+ InterfacePtr& operator=(InterfacePtr&& other) {
+ if (this != &other) {
+ impl_ = std::move(other.impl_);
+ other.impl_.reset(new Impl);
+ }
+ return *this;
+ }
+
+ // Bind the |InterfacePtr| to one endpoint of a newly created channel and
+ // return the other endpoint as an |InterfaceRequest|.
+ //
+ // Typically, the returned |InterfaceRequest| will be sent to a remote process
+ // to be bound to an implementation of |Interface| using a |Binding| object.
+ //
+ // After calling this method, clients can start calling methods on this
+ // |InterfacePtr|. The methods will write messages into the underlying
+ // channel created by |NewRequest|, where they will be buffered by the
+ // underlying channel until the |InterfaceRequest| is bound to an
+ // implementation of |Interface|, potentially in a remote process.
+ //
+ // Uses the given async_t (e.g., a message loop) in order to read messages
+ // from the channel and to monitor the channel for |ZX_CHANNEL_PEER_CLOSED|.
+ // If |dispatcher| is null, the current thread must have a default async_t.
+ //
+ // # Example
+ //
+ // Given the following interface:
+ //
+ // interface Database {
+ // OpenTable(request<Table> table);
+ // };
+ //
+ // The client can use the |NewRequest| method to create the |InterfaceRequest|
+ // object needed by the |OpenTable| method:
+ //
+ // DatabasePtr database = ...; // Connect to database.
+ // TablePtr table;
+ // database->OpenTable(table.NewRequest());
+ //
+ // The client can call methods on |table| immediately.
+ InterfaceRequest<Interface> NewRequest(
+ async_dispatcher_t* dispatcher = nullptr) {
+ zx::channel h1;
+ zx::channel h2;
+ if (zx::channel::create(0, &h1, &h2) != ZX_OK ||
+ Bind(std::move(h1), dispatcher) != ZX_OK)
+ return nullptr;
+ return InterfaceRequest<Interface>(std::move(h2));
+ }
+
+ // Binds the |InterfacePtr| to the given |channel|.
+ //
+ // The |InterfacePtr| expects the remote end of the |channel| to speak the
+ // protocol defined by |Interface|. Unlike the |Bind| overload that takes a
+ // |InterfaceHandle| parameter, this |Bind| overload lacks type safety.
+ //
+ // If the |InterfacePtr| was prevously bound to another channel, that channel
+ // is closed. If the |channel| is invalid, then this method will effectively
+ // unbind the |InterfacePtr|. A more direct way to have that effect is to call
+ // |Unbind|.
+ //
+ // Uses the given async_t (e.g., a message loop) in order to read messages
+ // from the channel and to monitor the channel for |ZX_CHANNEL_PEER_CLOSED|.
+ // If |dispatcher| is null, the current thread must have a default async_t.
+ //
+ // Returns an error if the binding was not able to be created (e.g., because
+ // the |channel| lacks |ZX_RIGHT_WAIT|).
+ zx_status_t Bind(zx::channel channel,
+ async_dispatcher_t* dispatcher = nullptr) {
+ return impl_->controller.reader().Bind(std::move(channel), dispatcher);
+ }
+
+ // Binds the |InterfacePtr| to the given |InterfaceHandle|.
+ //
+ // The |InterfacePtr| expects the remote end of the |channel| to speak the
+ // protocol defined by |Interface|. Unlike the |Bind| overload that takes a
+ // |channel| parameter, this |Bind| overload provides type safety.
+ //
+ // If the |InterfacePtr| was prevously bound to another channel, that channel
+ // is closed. If the |InterfaceHandle| is invalid, then this method will
+ // effectively unbind the |InterfacePtr|. A more direct way to have that
+ // effect is to call |Unbind|.
+ //
+ // Uses the given async_t (e.g., a message loop) in order to read messages
+ // from the channel and to monitor the channel for |ZX_CHANNEL_PEER_CLOSED|.
+ // If |dispatcher| is null, the current thread must have a default async_t.
+ //
+ // Returns an error if the binding was not able to be created (e.g., because
+ // the |channel| lacks |ZX_RIGHT_WAIT|).
+ zx_status_t Bind(InterfaceHandle<Interface> handle,
+ async_dispatcher_t* dispatcher = nullptr) {
+ return Bind(handle.TakeChannel(), dispatcher);
+ }
+
+ // Unbinds the underlying channel from the |InterfacePtr|.
+ //
+ // The underlying channel is returned as an |InterfaceHandle|, which is safe
+ // to transport to another thread or process. Any callbacks waiting for
+ // replies from the remote endpoint are discarded and any outstanding
+ // transaction state is erased.
+ //
+ // After this method returns, a subsequent call to |Bind| is required before
+ // calling any additional |Interface| methods.
+ InterfaceHandle<Interface> Unbind() {
+ return InterfaceHandle<Interface>(impl_->controller.reader().Unbind());
+ }
+
+ // Whether this |InterfacePtr| is currently bound to a channel.
+ //
+ // If the |InterfacePtr| is bound to a channel, the |InterfacePtr| has
+ // affinity for the thread on which it was bound and calls to |Interface|
+ // methods are proxied to the remote endpoint of the channel.
+ //
+ // See also:
+ //
+ // * |Bind|, which binds a channel to this |InterfacePtr|.
+ // * |Unbind|, which unbinds a channel from this |InterfacePtr|.
+ bool is_bound() const { return impl_->controller.reader().is_bound(); }
+
+ // Whether this |InterfacePtr| is currently bound to a channel.
+ //
+ // See |is_bound| for details.
+ explicit operator bool() const { return is_bound(); }
+
+ // The |Interface| proxy associated with this |InterfacePtr|.
+ //
+ // When this |InterfacePtr| is bound, method calls on this |Interface| will
+ // be proxied to the remote endpoint of the connection. Methods that expect
+ // replies will retain the supplied callbacks until the |InterfacePtr| either
+ // receives a reply to that transaction or the |InterfacePtr| is unbound from
+ // the channel.
+ //
+ // When this |InterfacePtr| is not bound, it is an error to call methods on
+ // the returned |Interface|.
+ //
+ // The returned |Interface| is thread-hostile and can be used only on the
+ // thread on which the |InterfacePtr| was bound.
+ Interface* get() const { return &impl_->proxy; }
+ Interface* operator->() const { return get(); }
+ Interface& operator*() const { return *get(); }
+
+ // An object on which to register for FIDL events.
+ //
+ // Arriving events are dispatched to the callbacks stored on this object.
+ // Events for unbound callbacks are ignored.
+ Proxy& events() const { return impl_->proxy; }
+
+#ifdef FIDL_ENABLE_LEGACY_WAIT_FOR_RESPONSE
+
+ // DEPRECATED: Using InterfaceSyncPtr instead. If used in a test, consider
+ // spinning the async::Loop instead.
+ //
+ // Blocks the calling thread until either a message arrives on the previously
+ // bound channel or an error occurs.
+ //
+ // Returns an error if waiting for the message, reading the message, or
+ // processing the message fails. If the error results in the channel being
+ // closed, the error handler will be called synchronously before this
+ // method returns.
+ //
+ // This method can be called only if this |InterfacePtr| is currently bound to
+ // a channel.
+ zx_status_t WaitForResponse() {
+ return WaitForResponseUntil(zx::time::infinite());
+ }
+
+ // DEPRECATED: Using InterfaceSyncPtr instead. If used in a test, consider
+ // spinning the async::Loop instead.
+ //
+ // Blocks the calling thread until either a message arrives on the previously
+ // bound channel, an error occurs, or the |deadline| is exceeded.
+ //
+ // Returns ZX_ERR_TIMED_OUT if the deadline is exceeded.
+ //
+ // Returns an error if waiting for the message, reading the message, or
+ // processing the message fails. If the error results in the channel being
+ // closed, the error handler will be called synchronously before this
+ // method returns.
+ //
+ // This method can be called only if this |InterfacePtr| is currently bound to
+ // a channel.
+ zx_status_t WaitForResponseUntil(zx::time deadline) {
+ return impl_->controller.reader().WaitAndDispatchOneMessageUntil(deadline);
+ }
+#endif
+
+ // Sets an error handler that will be called if an error causes the underlying
+ // channel to be closed.
+ //
+ // If the error is being reported because an error occurred on the local side
+ // of the channel, the zx_status_t of that error will be passed as the
+ // parameter to the handler.
+ //
+ // If an Epitaph was present on the channel, its error value will be passed as
+ // the parameter. See the FIDL language specification for more detail on
+ // Epitaphs.
+ //
+ // For example, the error handler will be called if the remote side of the
+ // channel sends an invalid message. When the error handler is called, the
+ // |Binding| will no longer be bound to the channel.
+ void set_error_handler(fit::function<void(zx_status_t)> error_handler) {
+ impl_->controller.reader().set_error_handler(std::move(error_handler));
+ }
+
+ // The underlying channel.
+ const zx::channel& channel() const {
+ return impl_->controller.reader().channel();
+ }
+
+ // Closes the channel and sends an Epitaph with the given error. See the FIDL
+ // language spec for information about Epitaphs.
+ //
+ // The return value can be any of the return values of zx_channel_write.
+ zx_status_t Close(zx_status_t epitaph_value) {
+ return impl_->controller.reader().Close(epitaph_value);
+ }
+
+ private:
+ struct Impl;
+
+ std::unique_ptr<Impl> impl_;
+};
+
+template <typename T>
+struct InterfacePtr<T>::Impl {
+ Impl() : proxy(&controller) { controller.set_proxy(&proxy); }
+ internal::ProxyController controller;
+ mutable Proxy proxy;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERFACE_PTR_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr_set.h b/pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr_set.h
new file mode 100644
index 0000000..8d54420
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr_set.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERFACE_PTR_SET_H_
+#define LIB_FIDL_CPP_INTERFACE_PTR_SET_H_
+
+#include <zircon/assert.h>
+#include <vector>
+
+#include "lib/fidl/cpp/interface_ptr.h"
+
+namespace fidl {
+
+// Contains a set of |InterfacePtr| objects, each with their own channels.
+//
+// An |InterfacePtr| is removed from the set and destroyed when its underlying
+// channel experiences an error. When the set is destroyed, all the underlying
+// channels are closed.
+//
+// An |InterfacePtrSet| is useful for broadcasting messages to a set of clients,
+// each with their own implementation of |Interface|.
+//
+// See also:
+//
+// * |BindingSet|, which is the server analog of an |InterfacePtrSet|.
+template <typename Interface>
+class InterfacePtrSet {
+ public:
+ using StorageType = std::vector<std::unique_ptr<InterfacePtr<Interface>>>;
+ using Ptr = InterfacePtr<Interface>;
+
+ // Creates an empty |InterfacePtrSet|.
+ InterfacePtrSet() = default;
+
+ InterfacePtrSet(const InterfacePtrSet& other) = delete;
+ InterfacePtrSet& operator=(const InterfacePtrSet& other) = delete;
+
+ // Adds the given |InterfacePtr| to the set.
+ //
+ // The |InterfacePtr| must already be bound to a channel. The |InterfacePtr|
+ // will be removed from the set when its underlying channel experiences an
+ // error.
+ void AddInterfacePtr(InterfacePtr<Interface> ptr) {
+ ZX_DEBUG_ASSERT(ptr.is_bound());
+ // Allocated a new |InterfacePtr| so that we can have a unique value to use
+ // as a key for removing the InterfacePtr. Otherwise, we'll lose track of
+ // the InterfacePtr when the vector resizes.
+ ptrs_.push_back(std::make_unique<Ptr>(std::move(ptr)));
+ auto* pointer = ptrs_.back().get();
+ pointer->set_error_handler(
+ [pointer, this](zx_status_t status) { this->RemoveOnError(pointer); });
+ }
+
+ // The |InterfacePtr| objects stored in this set.
+ //
+ // This collection of bindings can be invalidated when an |InterfacePtr| in
+ // the set encounters a connection error because connection errors causes the
+ // |InterfacePtrSet| to remove the |InterfacePtr| from the set.
+ const StorageType& ptrs() const { return ptrs_; }
+
+ // Closes all channels associated with |InterfacePtr| objects in the set.
+ //
+ // After this method returns, the set is empty.
+ void CloseAll() { ptrs_.clear(); }
+
+ // The number of |InterfacePtr| objects in the set.
+ //
+ // This number might be smaller than the number of |InterfacePtr| objects
+ // added to the set if some of the underlying channels have experienced an
+ // error.
+ size_t size() const { return ptrs_.size(); }
+
+ private:
+ // Removes the given |pointer| from the set.
+ //
+ // Called from the error handler callback for |pointer|.
+ void RemoveOnError(Ptr* pointer) {
+ auto it = std::find_if(ptrs_.begin(), ptrs_.end(),
+ [pointer](const std::unique_ptr<Ptr>& p) {
+ return p.get() == pointer;
+ });
+ ZX_DEBUG_ASSERT(it != ptrs_.end());
+ ptrs_.erase(it);
+ }
+
+ // We use |unique_ptr| rather than just |InterfacePtr| so that we can keep
+ // track of the |InterfacePtr| objects after the |vector| resizes and moves
+ // its contents to its new buffer.
+ StorageType ptrs_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERFACE_PTR_SET_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/header.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/header.h
new file mode 100644
index 0000000..60ec272
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/header.h
@@ -0,0 +1,62 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_HEADER_H_
+#define LIB_FIDL_CPP_INTERNAL_HEADER_H_
+
+#include <functional>
+#include <ostream>
+
+#include <lib/fidl/cpp/array.h>
+#include <lib/fit/function.h>
+
+#ifdef __Fuchsia__
+#include <lib/zx/bti.h>
+#include <lib/zx/channel.h>
+#include <lib/zx/debuglog.h>
+#include <lib/zx/event.h>
+#include <lib/zx/eventpair.h>
+#include <lib/zx/fifo.h>
+#include <lib/zx/guest.h>
+#include <lib/zx/handle.h>
+#include <lib/zx/interrupt.h>
+#include <lib/zx/job.h>
+#include <lib/zx/log.h>
+#include <lib/zx/object.h>
+#include <lib/zx/pmt.h>
+#include <lib/zx/port.h>
+#include <lib/zx/process.h>
+#include <lib/zx/profile.h>
+#include <lib/zx/resource.h>
+#include <lib/zx/socket.h>
+#include <lib/zx/suspend_token.h>
+#include <lib/zx/task.h>
+#include <lib/zx/thread.h>
+#include <lib/zx/time.h>
+#include <lib/zx/timer.h>
+#include <lib/zx/vcpu.h>
+#include <lib/zx/vmar.h>
+#include <lib/zx/vmo.h>
+#endif
+
+#include "lib/fidl/cpp/coding_traits.h"
+#include "lib/fidl/cpp/enum.h"
+
+#ifdef __Fuchsia__
+#include "lib/fidl/cpp/interface_ptr.h"
+#include "lib/fidl/cpp/internal/proxy_controller.h"
+#include "lib/fidl/cpp/internal/stub_controller.h"
+#include "lib/fidl/cpp/internal/synchronous_proxy.h"
+#include "lib/fidl/cpp/synchronous_interface_ptr.h"
+#endif
+
+#include "lib/fidl/cpp/string.h"
+#include "lib/fidl/cpp/vector.h"
+
+// clone.h must be imported before any of the generated Clone methods are
+// defined, so that calls to Clone in clone.h are referencing the ADL
+// implementation and are not ambiguous.
+#include "lib/fidl/cpp/clone.h"
+
+#endif // LIB_FIDL_CPP_INTERNAL_HEADER_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/implementation.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/implementation.h
new file mode 100644
index 0000000..37a3bec
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/implementation.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_IMPLEMENTATION_H_
+#define LIB_FIDL_CPP_INTERNAL_IMPLEMENTATION_H_
+
+#include <lib/fidl/internal.h>
+#include <stdint.h>
+#include <zircon/assert.h>
+
+#include <limits>
+#include <memory>
+
+#include "lib/fidl/cpp/clone.h"
+#include "lib/fidl/cpp/comparison.h"
+#include "lib/fidl/cpp/encoder.h"
+#include "lib/fidl/cpp/internal/logging.h"
+#include "lib/fidl/cpp/traits.h"
+
+#endif // LIB_FIDL_CPP_INTERNAL_IMPLEMENTATION_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/message_handler.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/message_handler.h
new file mode 100644
index 0000000..13660ad
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/message_handler.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_MESSAGE_HANDLER_H_
+#define LIB_FIDL_CPP_INTERNAL_MESSAGE_HANDLER_H_
+
+#include <lib/fidl/cpp/message.h>
+#include <zircon/types.h>
+
+namespace fidl {
+namespace internal {
+
+// An interface for receiving FIDL messages.
+//
+// Used by |MessageReader| to call back into its client whenever it reads a
+// message from the channel.
+class MessageHandler {
+ public:
+ virtual ~MessageHandler();
+
+ // A new message has arrived.
+ //
+ // The memory backing the message will remain valid until this method returns,
+ // at which point the memory might or might not be deallocated.
+ virtual zx_status_t OnMessage(Message message) = 0;
+
+ // The channel from which the messages were being read is gone.
+ //
+ // The channel's peer might have been closed or the |MessageReader| might have
+ // unbound from the channel. In either case, implementations that keep
+ // per-channel state should reset their state when this method is called.
+ virtual void OnChannelGone();
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_MESSAGE_HANDLER_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/message_reader.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/message_reader.h
new file mode 100644
index 0000000..5272231
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/message_reader.h
@@ -0,0 +1,147 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_MESSAGE_READER_H_
+#define LIB_FIDL_CPP_INTERNAL_MESSAGE_READER_H_
+
+#include <lib/async/wait.h>
+#include <lib/fidl/cpp/message.h>
+#include <lib/fidl/cpp/message_buffer.h>
+#include <lib/fit/function.h>
+#include <lib/zx/channel.h>
+
+#include <functional>
+#include <memory>
+#include <utility>
+
+#include "lib/fidl/cpp/internal/message_handler.h"
+
+namespace fidl {
+namespace internal {
+
+class MessageReader {
+ public:
+ explicit MessageReader(MessageHandler* message_handler = nullptr);
+ ~MessageReader();
+
+ MessageReader(const MessageReader&) = delete;
+ MessageReader& operator=(const MessageReader&) = delete;
+
+ // Binds the given channel to this |MessageReader|.
+ //
+ // The |MessageReader| will wait asynchronously for messages on this channel
+ // and dispatch them to the message handler using |dispatcher|. After this
+ // method returns, the |MessageReader| will be waiting for incomming messages.
+ //
+ // If the |MessageReader| is already bound, the |MessageReader| will first
+ // be unbound.
+ //
+ // If |dispatcher| is null, the current thread must have a default async_t.
+ zx_status_t Bind(zx::channel channel,
+ async_dispatcher_t* dispatcher = nullptr);
+
+ // Unbinds the channel from this |MessageReader|.
+ //
+ // The |MessageReader| will stop waiting for the messages on this channel.
+ //
+ // Returns the channel to which this |MessageReader| was previously bound, if
+ // any.
+ zx::channel Unbind();
+
+ // Unbinds the channel from this |MessageReader| and clears the error handler.
+ void Reset();
+
+ // Unbinds the channel from |other| and bindings it to this |MessageReader|.
+ //
+ // Also moves the error handler from |other| to this |MessageReader|.
+ //
+ // Useful for implementing move semantics for objects that have a
+ // |MessageReader|.
+ zx_status_t TakeChannelAndErrorHandlerFrom(MessageReader* other);
+
+ // Sends an epitaph with the given value, unbinds, and then closes the channel
+ // associated with this |MessageReader|.
+ //
+ // The |MessageReader| will send an Epitaph with the given error, unbind
+ // the channel, and then close it.
+ //
+ // The return value can be any of the return values of zx_channel_write.
+ zx_status_t Close(zx_status_t epitaph_value);
+
+ // Whether the |MessageReader| is currently bound.
+ //
+ // See |Bind()| and |Unbind()|.
+ bool is_bound() const { return channel_.is_valid(); }
+
+ // The channel to which this |MessageReader| is bound, if any.
+ const zx::channel& channel() const { return channel_; }
+
+ // Synchronously waits on |channel()| until either a message is available or
+ // the peer closes. If the channel is readable, reads a single message from
+ // the channel and dispatches it to the message handler.
+ //
+ // Returns |ZX_ERR_BAD_STATE| if this |MessageReader| is not bound, or if it
+ // receives a malformed Epitaph.
+ zx_status_t WaitAndDispatchOneMessageUntil(zx::time deadline);
+
+ // The given message handler is called whenever the |MessageReader| reads a
+ // message from the channel.
+ //
+ // The |Message| given to the message handler will be valid until the message
+ // handler returns.
+ //
+ // The handler should return ZX_OK if the message was handled and an error
+ // otherwise. If the handler returns ZX_OK, the |MessageReader| will continue
+ // to wait for messages.
+ //
+ // The handler can destroy the |MessageReader|, in which case the
+ // handler MUST return |ZX_ERR_STOP|. If the handler returns
+ // |ZX_ERR_SHOULD_WAIT|, the |MessageReader| will continue waiting. Other
+ // errors cause the |MessageReader| to unbind from the channel and call the
+ // error handler.
+ void set_message_handler(MessageHandler* message_handler) {
+ message_handler_ = message_handler;
+ }
+
+ // The given error handler is called whenever the |MessageReader| encounters
+ // an error on the channel.
+ //
+ // If the error is being reported because an error occurred on the local side
+ // of the channel, the zx_status_t of that error will be passed as the
+ // parameter to the handler.
+ //
+ // If an Epitaph was present on the channel, its error value will be passed as
+ // the parameter. See the FIDL language specification for more detail on
+ // Epitaphs.
+ //
+ // For example, the error handler will be called if the remote side of the
+ // channel sends an invalid message. When the error handler is called, the
+ // |Binding| will no longer be bound to the channel.
+ //
+ // The handler can destroy the |MessageReader|.
+ void set_error_handler(fit::function<void(zx_status_t)> error_handler) {
+ error_handler_ = std::move(error_handler);
+ }
+
+ private:
+ static void CallHandler(async_dispatcher_t* dispatcher, async_wait_t* wait,
+ zx_status_t status, const zx_packet_signal_t* signal);
+ void OnHandleReady(async_dispatcher_t* dispatcher, zx_status_t status,
+ const zx_packet_signal_t* signal);
+ zx_status_t ReadAndDispatchMessage(MessageBuffer* buffer);
+ void NotifyError(zx_status_t epitaph_value);
+ void Stop();
+
+ async_wait_t wait_; // Must be first.
+ zx::channel channel_;
+ async_dispatcher_t* dispatcher_;
+ bool* should_stop_; // See |Canary| in message_reader.cc.
+ MessageHandler* message_handler_;
+ fit::function<void(zx_status_t)> error_handler_;
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_MESSAGE_READER_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/pending_response.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/pending_response.h
new file mode 100644
index 0000000..125a7d8
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/pending_response.h
@@ -0,0 +1,89 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_PENDING_RESPONSE_H_
+#define LIB_FIDL_CPP_INTERNAL_PENDING_RESPONSE_H_
+
+#include <lib/fidl/cpp/message_builder.h>
+#include <zircon/types.h>
+
+namespace fidl {
+namespace internal {
+class WeakStubController;
+
+// A response to a FIDL message.
+//
+// When a server receives a message that expects a response, the stub receives a
+// |PendingResponse| object that the implementation can use to reply to the
+// message. A given |PendingResponse| object can be used to send a reply at
+// most once.
+//
+// If the |StubController| that processed the original message is destroyed or
+// unbound from the underlying channel (e.g., due to an error), the stub can
+// still safely call |Send|, but the response will not actually be sent to the
+// client.
+class PendingResponse {
+ public:
+ // Creates a |PendingResponse| that does not need a response.
+ //
+ // The |needs_response()| method will return false.
+ PendingResponse();
+
+ // Creates a |PendingResponse| for a message with the given transaction ID.
+ //
+ // The |PendingResponse| object will take a reference to |weak_controller|,
+ // which it releases in its destructor.
+ PendingResponse(zx_txid_t txid, WeakStubController* weak_controller);
+
+ ~PendingResponse();
+
+ // |PendingResponse| objects are copiable.
+ //
+ // Each copy refers to the same logical reponse, which means |Send| should be
+ // called at most once among all the copies.
+ //
+ // The reason |PendingResponse| objects are copiable is so that they can be
+ // held by an std::function, which is also copyable. Typically, a
+ // |PendingResponse| object is held as a member of another object that
+ // implements operator(), which can be wrapped by std::function.
+ PendingResponse(const PendingResponse& other);
+ PendingResponse& operator=(const PendingResponse& other);
+
+ // |PendingResponse| objects are movable.
+ //
+ // Moving a |PendingResponse| object is more efficient that copying it because
+ // moving avoid churning the reference count of the associated
+ // |WeakStubController|.
+ PendingResponse(PendingResponse&& other);
+ PendingResponse& operator=(PendingResponse&& other);
+
+ // Whether the message that caused this |PendingResponse| object to be created
+ // expects a response.
+ //
+ // This method does not indiciate whether a response has or has not already
+ // been sent. That state is difficult to track because |PendingResponse| is
+ // copiable.
+ bool needs_response() const { return txid_; }
+
+ // Send a response.
+ //
+ // This function should be called at most once among all the copies of a given
+ // |PendingResponse| object.
+ //
+ // If the associated |WeakStubController| is no longer available (e.g., if it
+ // has been destroyed), this function will return |ZX_ERR_BAD_STATE|.
+ zx_status_t Send(const fidl_type_t* type, Message message);
+
+ private:
+ // This class should be small enough to fit into the inline storage for an
+ // std::function to avoid allocating additional storage when processing
+ // messages. Currently, std::function has space for three pointers.
+ zx_txid_t txid_;
+ WeakStubController* weak_controller_;
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_PENDING_RESPONSE_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/proxy.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/proxy.h
new file mode 100644
index 0000000..cc72783
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/proxy.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_PROXY_H_
+#define LIB_FIDL_CPP_INTERNAL_PROXY_H_
+
+#include <lib/fidl/cpp/message.h>
+#include <zircon/types.h>
+
+namespace fidl {
+namespace internal {
+
+// An interface for sending FIDL messages to a remote implementation.
+class Proxy {
+ public:
+ virtual ~Proxy();
+
+ // A new message has arrived.
+ //
+ // The memory backing the message will remain valid until this method returns,
+ // at which point the memory might or might not be deallocated.
+ virtual zx_status_t Dispatch_(Message message) = 0;
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_PROXY_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/proxy_controller.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/proxy_controller.h
new file mode 100644
index 0000000..070613f
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/proxy_controller.h
@@ -0,0 +1,96 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_PROXY_CONTROLLER_H_
+#define LIB_FIDL_CPP_INTERNAL_PROXY_CONTROLLER_H_
+
+#include <lib/fidl/cpp/message.h>
+#include <lib/fidl/cpp/message_builder.h>
+
+#include <map>
+#include <memory>
+
+#include "lib/fidl/cpp/internal/message_handler.h"
+#include "lib/fidl/cpp/internal/message_reader.h"
+#include "lib/fidl/cpp/internal/proxy.h"
+
+namespace fidl {
+namespace internal {
+
+// Controls the client endpoint of a FIDL channel.
+//
+// A |ProxyController| controls the protocol-specific "proxy" object. Proxy
+// objects are used on the client endpoint of a FIDL channel to encode messages
+// into the channel and send them to the server endpoint, whose "stub" object
+// decodes them and dispatches them to an implementation of the protocol.
+class ProxyController : public MessageHandler {
+ public:
+ ProxyController();
+ ~ProxyController();
+
+ ProxyController(const ProxyController&) = delete;
+ ProxyController& operator=(const ProxyController&) = delete;
+
+ ProxyController(ProxyController&&);
+ ProxyController& operator=(ProxyController&&);
+
+ // The |MessageReader| that is listening for responses to messages sent by
+ // this object.
+ MessageReader& reader() { return reader_; }
+ const MessageReader& reader() const { return reader_; }
+
+ // The protocol-specific object that decodes messages and dispatches them to
+ // an implementation of the protocol.
+ //
+ // The proxy must be set to a non-null value before messages are read from the
+ // underlying channel. Typically, the caller will set a non-null proxy before
+ // binding a channel to the |MessageReader|.
+ Proxy* proxy() const { return proxy_; }
+ void set_proxy(Proxy* proxy) { proxy_ = proxy; }
+
+ // Send a message over the channel.
+ //
+ // If |response_handler| is non-null, the message will be assigned a
+ // transaction identifier before being encoded and sent over the channel. The
+ // |response_handler| will be retained by the |ProxyController| until the
+ // |ProxyController| recieves a response to the message, at which time the
+ // |ProxyController| will call the |OnMessage| method of the
+ // |response_handler|.
+ //
+ // Returns an error if the message fails to encode properly or if the message
+ // cannot be written to the channel.
+ zx_status_t Send(const fidl_type_t* type, Message message,
+ std::unique_ptr<MessageHandler> response_handler);
+
+ // Clears all the state associated with this |ProxyController|.
+ //
+ // After this method returns, the |ProxyController| is in the same state it
+ // would have been in if freshly constructed.
+ void Reset();
+
+ private:
+ // Called by the |MessageReader| when a message arrives on the channel from
+ // the server.
+ //
+ // The message might be a response to a previously sent message or an
+ // unsolicited event.
+ zx_status_t OnMessage(Message message) final;
+
+ // Causes the |ProxyController| to |ClearPendingHandlers()|.
+ void OnChannelGone() final;
+
+ // Causes the |ProxyController| to destroy all pending response handlers and
+ // reset its transition identifiers.
+ void ClearPendingHandlers();
+
+ MessageReader reader_;
+ Proxy* proxy_ = nullptr;
+ std::map<zx_txid_t, std::unique_ptr<MessageHandler>> handlers_;
+ zx_txid_t next_txid_;
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_PROXY_CONTROLLER_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/stub.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/stub.h
new file mode 100644
index 0000000..448f995
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/stub.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_STUB_H_
+#define LIB_FIDL_CPP_INTERNAL_STUB_H_
+
+#include <lib/fidl/cpp/message.h>
+#include <zircon/types.h>
+
+#include "lib/fidl/cpp/internal/pending_response.h"
+
+namespace fidl {
+namespace internal {
+class StubController;
+
+// An interface for dispatching FIDL messages to a protocol implementation.
+//
+// Used by |StubController| to supply both a |Message| and a |PendingResponse|
+// object to protocol implementations.
+class Stub {
+ public:
+ virtual ~Stub();
+
+ // A new message has arrived.
+ //
+ // If the message expects a response, the |PendingResponse| object's
+ // |needs_response()| method will return true.
+ //
+ // The memory backing the message will remain valid until this method returns,
+ // at which point the memory might or might not be deallocated.
+ //
+ // The |PendingResponse| object has affinity for the current thread and is not
+ // safe to transport to another thread.
+ virtual zx_status_t Dispatch_(Message message, PendingResponse response) = 0;
+
+ // The protocol-agnostic object that listens for incoming messages.
+ //
+ // The controller must be set to a non-null value before sending events
+ // through this stub.
+ StubController* controller() const { return controller_; }
+ void set_controller(StubController* controller) { controller_ = controller; }
+
+ private:
+ StubController* controller_ = nullptr;
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_STUB_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/stub_controller.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/stub_controller.h
new file mode 100644
index 0000000..d89729e
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/stub_controller.h
@@ -0,0 +1,80 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_STUB_CONTROLLER_H_
+#define LIB_FIDL_CPP_INTERNAL_STUB_CONTROLLER_H_
+
+#include <memory>
+
+#include <lib/fidl/cpp/message.h>
+#include <lib/zx/channel.h>
+
+#include "lib/fidl/cpp/internal/message_handler.h"
+#include "lib/fidl/cpp/internal/message_reader.h"
+#include "lib/fidl/cpp/internal/stub.h"
+
+namespace fidl {
+namespace internal {
+class WeakStubController;
+
+// Controls the server endpoint of a FIDL channel.
+//
+// A |StubController| controls the protocol-specific "stub" object. Stub
+// objects are used on the server endpoint of a FIDL channel to decode messages
+// received over the channel and dispatch them to an implementation of the
+// protocol.
+class StubController : public MessageHandler {
+ public:
+ StubController();
+ ~StubController();
+
+ StubController(const StubController&) = delete;
+ StubController& operator=(const StubController&) = delete;
+
+ // The |MessageReader| that is listening for messages sent by the client.
+ MessageReader& reader() { return reader_; }
+ const MessageReader& reader() const { return reader_; }
+
+ // The protocol-specific object that decodes messages and dispatches them to
+ // an implementation of the protocol.
+ //
+ // The stub must be set to a non-null value before messages are read from the
+ // underlying channel. Typically, the caller will set a non-null stub before
+ // binding a channel to the |MessageReader|.
+ Stub* stub() const { return stub_; }
+ void set_stub(Stub* stub) { stub_ = stub; }
+
+ // Send a message over the channel.
+ //
+ // Returns an error if the message fails to encode properly or if the message
+ // cannot be written to the channel.
+ zx_status_t Send(const fidl_type_t* type, Message message);
+
+ private:
+ // Called by the |MessageReader| when a message arrives on the channel from
+ // the client.
+ //
+ // The message will be dispatched using the |stub()|. If the message expects a
+ // response, the |stub()| will also be given a |PendingResponse| object that
+ // can be used to send a reply to the message.
+ zx_status_t OnMessage(Message message) final;
+
+ // Causes the |StubController| to invalidate all outstanding weak pointers,
+ // preventing outstanding |PendingResponse| objects from sending messages on
+ // the channel that has gone away.
+ void OnChannelGone() final;
+
+ // Invalidate all outstanding weak pointers, preventing outstanding
+ // |PendingResponse| objects from sending messages.
+ void InvalidateWeakIfNeeded();
+
+ WeakStubController* weak_;
+ MessageReader reader_;
+ Stub* stub_;
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_STUB_CONTROLLER_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/internal/weak_stub_controller.h b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/weak_stub_controller.h
new file mode 100644
index 0000000..cdfbb1d
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/internal/weak_stub_controller.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_WEAK_STUB_CONTROLLER_H_
+#define LIB_FIDL_CPP_INTERNAL_WEAK_STUB_CONTROLLER_H_
+
+#include <stdint.h>
+
+namespace fidl {
+namespace internal {
+class StubController;
+
+// A weak reference to a |StubController|.
+//
+// Used to link a |PendingResponse| object with a |StubController|. When the
+// |StubController| is destroyed (or unbound from the underling channel), the
+// weak reference is invalidated, preventing outstanding |PendingResponse|
+// objects from referencing the |StubController|.
+class WeakStubController {
+ public:
+ // Creates a weak reference to a |StubController|.
+ //
+ // The created |WeakStubController| has a reference count of one, which means
+ // the creator is responsible for calling |Release| exactly once.
+ explicit WeakStubController(StubController* controller);
+
+ // Increment the refernence count for this object.
+ //
+ // Each call to this method imposes a requirement to eventually call |Release|
+ // exactly once.
+ void AddRef();
+
+ // Decrements the reference count for this object.
+ //
+ // When the reference count reaches zero, the object is destroyed.
+ void Release();
+
+ // Break the connection between this object and the |StubController|.
+ //
+ // After calling this method, |controller()| will return nullptr.
+ void Invalidate();
+
+ // The |StubController| to which this weak reference refers.
+ //
+ // After the weak reference has been invalidated, this method returns nullptr.
+ StubController* controller() const { return controller_; }
+
+ private:
+ ~WeakStubController();
+
+ uint32_t ref_count_; // starts at one
+ StubController* controller_;
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_WEAK_STUB_CONTROLLER_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/optional.h b/pkg/fidl_cpp/include/lib/fidl/cpp/optional.h
new file mode 100644
index 0000000..4dc6219
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/optional.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_OPTIONAL_H_
+#define LIB_FIDL_CPP_OPTIONAL_H_
+
+#include <memory>
+#include <utility>
+
+namespace fidl {
+
+template <typename T>
+std::unique_ptr<T> MakeOptional(T value) {
+ return std::make_unique<T>(std::move(value));
+}
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_OPTIONAL_H_
diff --git a/pkg/fidl_cpp/include/lib/fidl/cpp/thread_safe_binding_set.h b/pkg/fidl_cpp/include/lib/fidl/cpp/thread_safe_binding_set.h
new file mode 100644
index 0000000..a3b572c
--- /dev/null
+++ b/pkg/fidl_cpp/include/lib/fidl/cpp/thread_safe_binding_set.h
@@ -0,0 +1,127 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_THREAD_SAFE_BINDING_SET_H_
+#define LIB_FIDL_CPP_THREAD_SAFE_BINDING_SET_H_
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+#include <lib/async/dispatcher.h>
+#include <zircon/compiler.h>
+
+#include "lib/fidl/cpp/binding.h"
+
+namespace fidl {
+
+// Manages a set of bindings to implemenations owned by the bound channels.
+//
+// The implementation pointer type of the binding is also parameterized,
+// allowing the use of smart pointer types such as |std::unique_ptr<>| to
+// reference the implementation.
+//
+// This class is thread-safe; bindings may be added or cleared from any thread.
+//
+// See also:
+//
+// * |BindingSet|, which is the thread-hostile analog that offers more
+// functionality.
+// * |InterfacePtrSet|, which is the client analog of |BindingSet|.
+template <typename Interface, typename ImplPtr = Interface*>
+class ThreadSafeBindingSet {
+ public:
+ using Binding = ::fidl::Binding<Interface, ImplPtr>;
+ using StorageType = std::vector<std::unique_ptr<Binding>>;
+
+ ThreadSafeBindingSet() = default;
+
+ ThreadSafeBindingSet(const ThreadSafeBindingSet&) = delete;
+ ThreadSafeBindingSet& operator=(const ThreadSafeBindingSet&) = delete;
+
+ // Adds a binding to the set.
+ //
+ // The given |ImplPtr| is bound to the channel underlying the
+ // |InterfaceRequest|. The binding is removed (and the |~ImplPtr| called)
+ // when the created binding has an error (e.g., if the remote endpoint of
+ // the channel sends an invalid message).
+ //
+ // Whether this method takes ownership of |impl| depends on |ImplPtr|. If
+ // |ImplPtr| is a raw pointer, then this method does not take ownership of
+ // |impl|. If |ImplPtr| is a |unique_ptr|, then running |~ImplPtr| when the
+ // binding generates an error will delete |impl| because |~ImplPtr| is
+ // |~unique_ptr|, which deletes |impl|.
+ //
+ // The impl will use the given async_t (e.g., a message loop) in order to read
+ // messages from the channel and to monitor the channel for
+ // |ZX_CHANNEL_PEER_CLOSED|. It is not necessary to use the same async_t for
+ // each binding added.
+ void AddBinding(ImplPtr impl, InterfaceRequest<Interface> request,
+ async_dispatcher_t* dispatcher) {
+ std::lock_guard<std::mutex> guard(lock_);
+ bindings_.push_back(std::make_unique<Binding>(
+ std::forward<ImplPtr>(impl), std::move(request), dispatcher));
+ auto* binding = bindings_.back().get();
+ // Set the connection error handler for the newly added Binding to be a
+ // function that will erase it from the vector.
+ binding->set_error_handler(
+ [binding, this](zx_status_t status) { this->RemoveOnError(binding); });
+ }
+
+ // Adds a binding to the set for the given implementation.
+ //
+ // Creates a channel for the binding and returns the client endpoint of
+ // the channel as an |InterfaceHandle|. If |AddBinding| fails to create the
+ // underlying channel, the returned |InterfaceHandle| will return false from
+ // |is_valid()|.
+ //
+ // The given |ImplPtr| is bound to the newly created channel. The binding is
+ // removed (and the |~ImplPtr| called) when the created binding has an error
+ // (e.g., if the remote endpoint of the channel sends an invalid message).
+ //
+ // Whether this method takes ownership of |impl| depends on |ImplPtr|. If
+ // |ImplPtr| is a raw pointer, then this method does not take ownership of
+ // |impl|. If |ImplPtr| is a |unique_ptr|, then running |~ImplPtr| when the
+ // binding generates an error will delete |impl| because |~ImplPtr| is
+ // |~unique_ptr|, which deletes |impl|.
+ InterfaceHandle<Interface> AddBinding(ImplPtr impl,
+ async_dispatcher_t* dispatcher) {
+ InterfaceHandle<Interface> handle;
+ InterfaceRequest<Interface> request = handle.NewRequest();
+ if (!request)
+ return nullptr;
+ AddBinding(std::forward<ImplPtr>(impl), std::move(request), dispatcher);
+ return handle;
+ }
+
+ // Removes all the bindings from the set.
+ //
+ // Closes all the channels associated with this |BindingSet|.
+ void CloseAll() {
+ std::lock_guard<std::mutex> guard(lock_);
+ bindings_.clear();
+ }
+
+ private:
+ // Called when a binding has an error to remove the binding from the set.
+ void RemoveOnError(Binding* binding) {
+ std::lock_guard<std::mutex> guard(lock_);
+ auto it = std::find_if(bindings_.begin(), bindings_.end(),
+ [binding](const std::unique_ptr<Binding>& b) {
+ return b.get() == binding;
+ });
+ ZX_DEBUG_ASSERT(it != bindings_.end());
+ (*it)->set_error_handler(nullptr);
+ bindings_.erase(it);
+ }
+
+ std::mutex lock_;
+ StorageType bindings_ __TA_GUARDED(lock_);
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_THREAD_SAFE_BINDING_SET_H_
diff --git a/pkg/fidl_cpp/internal/message_handler.cc b/pkg/fidl_cpp/internal/message_handler.cc
new file mode 100644
index 0000000..fbcaa5a
--- /dev/null
+++ b/pkg/fidl_cpp/internal/message_handler.cc
@@ -0,0 +1,15 @@
+// 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/fidl/cpp/internal/message_handler.h"
+
+namespace fidl {
+namespace internal {
+
+MessageHandler::~MessageHandler() = default;
+
+void MessageHandler::OnChannelGone() {}
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp/internal/message_reader.cc b/pkg/fidl_cpp/internal/message_reader.cc
new file mode 100644
index 0000000..e4331f7
--- /dev/null
+++ b/pkg/fidl_cpp/internal/message_reader.cc
@@ -0,0 +1,268 @@
+// 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/fidl/cpp/internal/message_reader.h"
+
+#include <lib/async/default.h>
+#include <lib/fidl/cpp/message_buffer.h>
+#include <lib/fidl/epitaph.h>
+#include <zircon/assert.h>
+
+namespace fidl {
+namespace internal {
+namespace {
+
+constexpr zx_signals_t kSignals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
+
+// |Canary| is a stack-allocated object that observes when a |MessageReader| is
+// destroyed or unbound from the current channel.
+//
+// Because |WaitAndDispatchOneMessageUntil| can be called re-entrantly, we can
+// be in a state where there are N nested calls to |ReadAndDispatchMessage| on
+// the stack. While dispatching any of those messages, the client can destroy
+// the |MessageReader| or unbind it from the current channel. When that happens
+// we need to stop reading messages from the channel and unwind the stack
+// safely.
+//
+// The |Canary| works by storing a pointer to its |should_stop_| field in the
+// |MessageReader|. Upon destruction or unbinding, the |MessageReader| writes
+// |true| into |should_stop_|. When we unwind the stack, the |Canary| forwards
+// that value to the next |Canary| on the stack.
+class Canary {
+ public:
+ explicit Canary(bool** should_stop_slot)
+ : should_stop_slot_(should_stop_slot),
+ previous_should_stop_(*should_stop_slot_),
+ should_stop_(false) {
+ *should_stop_slot_ = &should_stop_;
+ }
+
+ ~Canary() {
+ if (should_stop_) {
+ // If we should stop, we need to propagate that information to the
+ // |Canary| higher up the stack, if any. We also cannot touch
+ // |*should_stop_slot_| because the |MessageReader| might have been
+ // destroyed (or bound to another channel).
+ if (previous_should_stop_)
+ *previous_should_stop_ = should_stop_;
+ } else {
+ // Otherwise, the |MessageReader| was not destroyed and is still bound to
+ // the same channel. We need to restore the previous |should_stop_|
+ // pointer so that a |Canary| further up the stack can still be informed
+ // about whether to stop.
+ *should_stop_slot_ = previous_should_stop_;
+ }
+ }
+
+ // Whether the |ReadAndDispatchMessage| that created the |Canary| should stop
+ // after dispatching the current message.
+ bool should_stop() const { return should_stop_; }
+
+ private:
+ bool** should_stop_slot_;
+ bool* previous_should_stop_;
+ bool should_stop_;
+};
+
+} // namespace
+
+static_assert(std::is_standard_layout<MessageReader>::value,
+ "We need offsetof to work");
+
+MessageReader::MessageReader(MessageHandler* message_handler)
+ : wait_{{ASYNC_STATE_INIT},
+ &MessageReader::CallHandler,
+ ZX_HANDLE_INVALID,
+ kSignals},
+ dispatcher_(nullptr),
+ should_stop_(nullptr),
+ message_handler_(message_handler),
+ error_handler_(nullptr) {}
+
+MessageReader::~MessageReader() {
+ Stop();
+ if (dispatcher_)
+ async_cancel_wait(dispatcher_, &wait_);
+}
+
+zx_status_t MessageReader::Bind(zx::channel channel,
+ async_dispatcher_t* dispatcher) {
+ if (is_bound())
+ Unbind();
+ if (!channel)
+ return ZX_OK;
+ channel_ = std::move(channel);
+ if (dispatcher) {
+ dispatcher_ = dispatcher;
+ } else {
+ dispatcher_ = async_get_default_dispatcher();
+ }
+ ZX_ASSERT_MSG(dispatcher_ != nullptr,
+ "either |dispatcher| must be non-null, or "
+ "|async_get_default_dispatcher| must "
+ "be configured to return a non-null value");
+ wait_.object = channel_.get();
+ zx_status_t status = async_begin_wait(dispatcher_, &wait_);
+ if (status != ZX_OK)
+ Unbind();
+ return status;
+}
+
+zx::channel MessageReader::Unbind() {
+ if (!is_bound())
+ return zx::channel();
+ Stop();
+ async_cancel_wait(dispatcher_, &wait_);
+ wait_.object = ZX_HANDLE_INVALID;
+ dispatcher_ = nullptr;
+ zx::channel channel = std::move(channel_);
+ if (message_handler_)
+ message_handler_->OnChannelGone();
+ return channel;
+}
+
+void MessageReader::Reset() {
+ Unbind();
+ error_handler_ = nullptr;
+}
+
+zx_status_t MessageReader::TakeChannelAndErrorHandlerFrom(
+ MessageReader* other) {
+ zx_status_t status = Bind(other->Unbind(), other->dispatcher_);
+ if (status != ZX_OK)
+ return status;
+ error_handler_ = std::move(other->error_handler_);
+ return ZX_OK;
+}
+
+zx_status_t MessageReader::WaitAndDispatchOneMessageUntil(zx::time deadline) {
+ if (!is_bound())
+ return ZX_ERR_BAD_STATE;
+ zx_signals_t pending = ZX_SIGNAL_NONE;
+ zx_status_t status = channel_.wait_one(kSignals, deadline, &pending);
+ if (status == ZX_ERR_TIMED_OUT)
+ return status;
+ if (status != ZX_OK) {
+ NotifyError(status);
+ return status;
+ }
+
+ if (pending & ZX_CHANNEL_READABLE) {
+ MessageBuffer buffer;
+ return ReadAndDispatchMessage(&buffer);
+ }
+
+ ZX_DEBUG_ASSERT(pending & ZX_CHANNEL_PEER_CLOSED);
+ NotifyError(ZX_ERR_PEER_CLOSED);
+ return ZX_ERR_PEER_CLOSED;
+}
+
+void MessageReader::CallHandler(async_dispatcher_t* dispatcher,
+ async_wait_t* wait, zx_status_t status,
+ const zx_packet_signal_t* signal) {
+ static_assert(offsetof(MessageReader, wait_) == 0,
+ "The wait must be the first member for this cast to be valid.");
+ reinterpret_cast<MessageReader*>(wait)->OnHandleReady(dispatcher, status,
+ signal);
+}
+
+void MessageReader::OnHandleReady(async_dispatcher_t* dispatcher,
+ zx_status_t status,
+ const zx_packet_signal_t* signal) {
+ if (status != ZX_OK) {
+ NotifyError(status);
+ return;
+ }
+
+ if (signal->observed & ZX_CHANNEL_READABLE) {
+ MessageBuffer buffer;
+ for (uint64_t i = 0; i < signal->count; i++) {
+ status = ReadAndDispatchMessage(&buffer);
+ // If ReadAndDispatchMessage returns ZX_ERR_STOP, that means the message
+ // handler has destroyed this object and we need to unwind without
+ // touching |this|.
+ if (status == ZX_ERR_SHOULD_WAIT)
+ break;
+ if (status != ZX_OK)
+ return;
+ }
+ status = async_begin_wait(dispatcher, &wait_);
+ if (status != ZX_OK) {
+ NotifyError(status);
+ }
+ return;
+ }
+
+ ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED);
+ // Notice that we don't notify an error until we've drained all the messages
+ // out of the channel.
+ NotifyError(ZX_ERR_PEER_CLOSED);
+}
+
+zx_status_t MessageReader::ReadAndDispatchMessage(MessageBuffer* buffer) {
+ Message message = buffer->CreateEmptyMessage();
+ zx_status_t status = message.Read(channel_.get(), 0);
+ if (status == ZX_ERR_SHOULD_WAIT)
+ return status;
+ if (status != ZX_OK) {
+ NotifyError(status);
+ return status;
+ }
+
+ if (message.has_header() && message.ordinal() == FIDL_EPITAPH_ORDINAL) {
+ // This indicates the message is an epitaph, and that any epitaph-friendly
+ // error handlers should be invoked. Note the epitaph error is stored in
+ // the header's reserved word.
+
+ // TODO(FIDL-322): Use a different error code to distinguish remote encoding
+ // errors from local ones.
+ if (message.bytes().actual() != sizeof(fidl_epitaph_t)) {
+ NotifyError(ZX_ERR_INVALID_ARGS);
+ return ZX_ERR_INVALID_ARGS;
+ }
+ fidl_epitaph_t* epitaph = message.GetBytesAs<fidl_epitaph_t>();
+ NotifyError(epitaph->hdr.reserved0);
+ return ZX_ERR_PEER_CLOSED;
+ }
+
+ if (!message_handler_)
+ return ZX_OK;
+ Canary canary(&should_stop_);
+ status = message_handler_->OnMessage(std::move(message));
+ if (canary.should_stop())
+ return ZX_ERR_STOP;
+ if (status != ZX_OK)
+ NotifyError(status);
+ return status;
+}
+
+zx_status_t MessageReader::Close(zx_status_t epitaph_value) {
+ if (!is_bound()) {
+ return ZX_ERR_BAD_STATE;
+ }
+
+ zx_status_t status = fidl_epitaph_write(channel_.get(), epitaph_value);
+ if (status != ZX_OK) {
+ return status;
+ }
+ Unbind();
+ return ZX_OK;
+}
+
+void MessageReader::NotifyError(zx_status_t epitaph_value) {
+ Unbind();
+ if (error_handler_) {
+ error_handler_(epitaph_value);
+ }
+}
+
+void MessageReader::Stop() {
+ if (should_stop_) {
+ *should_stop_ = true;
+ should_stop_ = nullptr;
+ }
+}
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp/internal/pending_response.cc b/pkg/fidl_cpp/internal/pending_response.cc
new file mode 100644
index 0000000..3ac41ae
--- /dev/null
+++ b/pkg/fidl_cpp/internal/pending_response.cc
@@ -0,0 +1,77 @@
+// 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/fidl/cpp/internal/pending_response.h"
+
+#include "lib/fidl/cpp/internal/logging.h"
+#include "lib/fidl/cpp/internal/stub_controller.h"
+#include "lib/fidl/cpp/internal/weak_stub_controller.h"
+
+namespace fidl {
+namespace internal {
+
+PendingResponse::PendingResponse() : txid_(0), weak_controller_(nullptr) {}
+
+PendingResponse::PendingResponse(zx_txid_t txid,
+ WeakStubController* weak_controller)
+ : txid_(txid), weak_controller_(weak_controller) {
+ if (weak_controller_)
+ weak_controller_->AddRef();
+}
+
+PendingResponse::~PendingResponse() {
+ if (weak_controller_)
+ weak_controller_->Release();
+}
+
+PendingResponse::PendingResponse(const PendingResponse& other)
+ : PendingResponse(other.txid_, other.weak_controller_) {}
+
+PendingResponse& PendingResponse::operator=(const PendingResponse& other) {
+ if (this == &other)
+ return *this;
+ txid_ = other.txid_;
+ if (weak_controller_)
+ weak_controller_->Release();
+ weak_controller_ = other.weak_controller_;
+ if (weak_controller_)
+ weak_controller_->AddRef();
+ return *this;
+}
+
+PendingResponse::PendingResponse(PendingResponse&& other)
+ : txid_(other.txid_), weak_controller_(other.weak_controller_) {
+ other.weak_controller_ = nullptr;
+}
+
+PendingResponse& PendingResponse::operator=(PendingResponse&& other) {
+ if (this == &other)
+ return *this;
+ txid_ = other.txid_;
+ if (weak_controller_)
+ weak_controller_->Release();
+ weak_controller_ = other.weak_controller_;
+ other.weak_controller_ = nullptr;
+ return *this;
+}
+
+zx_status_t PendingResponse::Send(const fidl_type_t* type, Message message) {
+ if (!weak_controller_)
+ return ZX_ERR_BAD_STATE;
+ StubController* controller = weak_controller_->controller();
+ if (!controller)
+ return ZX_ERR_BAD_STATE;
+ message.set_txid(txid_);
+ const char* error_msg = nullptr;
+ zx_status_t status = message.Validate(type, &error_msg);
+ if (status != ZX_OK) {
+ FIDL_REPORT_ENCODING_ERROR(message, type, error_msg);
+ return status;
+ }
+ zx_handle_t channel = controller->reader().channel().get();
+ return message.Write(channel, 0);
+}
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp/internal/proxy.cc b/pkg/fidl_cpp/internal/proxy.cc
new file mode 100644
index 0000000..412aff7
--- /dev/null
+++ b/pkg/fidl_cpp/internal/proxy.cc
@@ -0,0 +1,13 @@
+// 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/fidl/cpp/internal/proxy.h"
+
+namespace fidl {
+namespace internal {
+
+Proxy::~Proxy() = default;
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp/internal/proxy_controller.cc b/pkg/fidl_cpp/internal/proxy_controller.cc
new file mode 100644
index 0000000..bec764c
--- /dev/null
+++ b/pkg/fidl_cpp/internal/proxy_controller.cc
@@ -0,0 +1,97 @@
+// 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/fidl/cpp/internal/proxy_controller.h"
+
+#include <utility>
+
+#include "lib/fidl/cpp/internal/logging.h"
+
+namespace fidl {
+namespace internal {
+namespace {
+
+constexpr uint32_t kUserspaceTxidMask = 0x7FFFFFFF;
+
+} // namespace
+
+ProxyController::ProxyController() : reader_(this), next_txid_(1) {}
+
+ProxyController::~ProxyController() = default;
+
+ProxyController::ProxyController(ProxyController&& other)
+ : reader_(this),
+ handlers_(std::move(other.handlers_)),
+ next_txid_(other.next_txid_) {
+ reader_.TakeChannelAndErrorHandlerFrom(&other.reader());
+ other.Reset();
+}
+
+ProxyController& ProxyController::operator=(ProxyController&& other) {
+ if (this != &other) {
+ reader_.TakeChannelAndErrorHandlerFrom(&other.reader());
+ handlers_ = std::move(other.handlers_);
+ next_txid_ = other.next_txid_;
+ other.Reset();
+ }
+ return *this;
+}
+
+zx_status_t ProxyController::Send(
+ const fidl_type_t* type, Message message,
+ std::unique_ptr<MessageHandler> response_handler) {
+ zx_txid_t txid = 0;
+ if (response_handler) {
+ txid = next_txid_++ & kUserspaceTxidMask;
+ while (!txid || handlers_.find(txid) != handlers_.end())
+ txid = next_txid_++ & kUserspaceTxidMask;
+ message.set_txid(txid);
+ }
+ const char* error_msg = nullptr;
+ zx_status_t status = message.Validate(type, &error_msg);
+ if (status != ZX_OK) {
+ FIDL_REPORT_ENCODING_ERROR(message, type, error_msg);
+ return status;
+ }
+ status = message.Write(reader_.channel().get(), 0);
+ if (status != ZX_OK) {
+ FIDL_REPORT_CHANNEL_WRITING_ERROR(message, type, status);
+ return status;
+ }
+ if (response_handler)
+ handlers_.emplace(txid, std::move(response_handler));
+ return ZX_OK;
+}
+
+void ProxyController::Reset() {
+ reader_.Reset();
+ ClearPendingHandlers();
+}
+
+zx_status_t ProxyController::OnMessage(Message message) {
+ if (!message.has_header())
+ return ZX_ERR_INVALID_ARGS;
+ zx_txid_t txid = message.txid();
+ if (!txid) {
+ if (!proxy_)
+ return ZX_ERR_NOT_SUPPORTED;
+ return proxy_->Dispatch_(std::move(message));
+ }
+ auto it = handlers_.find(txid);
+ if (it == handlers_.end())
+ return ZX_ERR_NOT_FOUND;
+ std::unique_ptr<MessageHandler> handler = std::move(it->second);
+ handlers_.erase(it);
+ return handler->OnMessage(std::move(message));
+}
+
+void ProxyController::OnChannelGone() { ClearPendingHandlers(); }
+
+void ProxyController::ClearPendingHandlers() {
+ handlers_.clear();
+ next_txid_ = 1;
+}
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp/internal/stub.cc b/pkg/fidl_cpp/internal/stub.cc
new file mode 100644
index 0000000..0ddec10
--- /dev/null
+++ b/pkg/fidl_cpp/internal/stub.cc
@@ -0,0 +1,13 @@
+// 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/fidl/cpp/internal/stub.h"
+
+namespace fidl {
+namespace internal {
+
+Stub::~Stub() = default;
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp/internal/stub_controller.cc b/pkg/fidl_cpp/internal/stub_controller.cc
new file mode 100644
index 0000000..2244229
--- /dev/null
+++ b/pkg/fidl_cpp/internal/stub_controller.cc
@@ -0,0 +1,52 @@
+// 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/fidl/cpp/internal/stub_controller.h"
+
+#include "lib/fidl/cpp/internal/logging.h"
+#include "lib/fidl/cpp/internal/pending_response.h"
+#include "lib/fidl/cpp/internal/weak_stub_controller.h"
+
+namespace fidl {
+namespace internal {
+
+StubController::StubController() : weak_(nullptr), reader_(this) {}
+
+StubController::~StubController() { InvalidateWeakIfNeeded(); }
+
+zx_status_t StubController::Send(const fidl_type_t* type, Message message) {
+ const char* error_msg = nullptr;
+ zx_status_t status = message.Validate(type, &error_msg);
+ if (status != ZX_OK) {
+ FIDL_REPORT_ENCODING_ERROR(message, type, error_msg);
+ return status;
+ }
+ return message.Write(reader_.channel().get(), 0);
+}
+
+zx_status_t StubController::OnMessage(Message message) {
+ if (!message.has_header())
+ return ZX_ERR_INVALID_ARGS;
+ zx_txid_t txid = message.txid();
+ WeakStubController* weak = nullptr;
+ if (txid) {
+ if (!weak_)
+ weak_ = new WeakStubController(this);
+ weak = weak_;
+ }
+ return stub_->Dispatch_(std::move(message), PendingResponse(txid, weak));
+}
+
+void StubController::OnChannelGone() { InvalidateWeakIfNeeded(); }
+
+void StubController::InvalidateWeakIfNeeded() {
+ if (!weak_)
+ return;
+ weak_->Invalidate();
+ weak_->Release();
+ weak_ = nullptr;
+}
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp/internal/weak_stub_controller.cc b/pkg/fidl_cpp/internal/weak_stub_controller.cc
new file mode 100644
index 0000000..7693ef7
--- /dev/null
+++ b/pkg/fidl_cpp/internal/weak_stub_controller.cc
@@ -0,0 +1,25 @@
+// 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/fidl/cpp/internal/weak_stub_controller.h"
+
+namespace fidl {
+namespace internal {
+
+WeakStubController::WeakStubController(StubController* controller)
+ : ref_count_(1u), controller_(controller) {}
+
+WeakStubController::~WeakStubController() = default;
+
+void WeakStubController::AddRef() { ++ref_count_; }
+
+void WeakStubController::Release() {
+ if (--ref_count_ == 0)
+ delete this;
+}
+
+void WeakStubController::Invalidate() { controller_ = nullptr; }
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp/meta.json b/pkg/fidl_cpp/meta.json
new file mode 100644
index 0000000..37f840f
--- /dev/null
+++ b/pkg/fidl_cpp/meta.json
@@ -0,0 +1,45 @@
+{
+ "deps": [
+ "fidl_cpp_sync",
+ "async",
+ "async-default",
+ "fidl",
+ "fidl-async",
+ "fit",
+ "zx"
+ ],
+ "fidl_deps": [],
+ "headers": [
+ "pkg/fidl_cpp/include/lib/fidl/cpp/binding.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/binding_set.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/enum.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/interface_ptr_set.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/header.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/implementation.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/message_handler.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/message_reader.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/pending_response.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/proxy.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/proxy_controller.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/stub.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/stub_controller.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/internal/weak_stub_controller.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/optional.h",
+ "pkg/fidl_cpp/include/lib/fidl/cpp/thread_safe_binding_set.h"
+ ],
+ "include_dir": "pkg/fidl_cpp/include",
+ "name": "fidl_cpp",
+ "root": "pkg/fidl_cpp",
+ "sources": [
+ "pkg/fidl_cpp/internal/message_handler.cc",
+ "pkg/fidl_cpp/internal/message_reader.cc",
+ "pkg/fidl_cpp/internal/pending_response.cc",
+ "pkg/fidl_cpp/internal/proxy.cc",
+ "pkg/fidl_cpp/internal/proxy_controller.cc",
+ "pkg/fidl_cpp/internal/stub.cc",
+ "pkg/fidl_cpp/internal/stub_controller.cc",
+ "pkg/fidl_cpp/internal/weak_stub_controller.cc"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/fidl_cpp_base/clone.cc b/pkg/fidl_cpp_base/clone.cc
new file mode 100644
index 0000000..9b4e80a
--- /dev/null
+++ b/pkg/fidl_cpp_base/clone.cc
@@ -0,0 +1,18 @@
+// 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/fidl/cpp/clone.h"
+
+namespace fidl {
+
+zx_status_t Clone(const StringPtr& value, StringPtr* result) {
+ if (!value) {
+ *result = StringPtr();
+ } else {
+ result->reset(*value);
+ }
+ return ZX_OK;
+}
+
+} // namespace fidl
diff --git a/pkg/fidl_cpp_base/coding_traits.cc b/pkg/fidl_cpp_base/coding_traits.cc
new file mode 100644
index 0000000..e66d8bd
--- /dev/null
+++ b/pkg/fidl_cpp_base/coding_traits.cc
@@ -0,0 +1,21 @@
+// 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/fidl/cpp/coding_traits.h"
+
+namespace fidl {
+
+void EncodeNullVector(Encoder* encoder, size_t offset) {
+ fidl_vector_t* vector = encoder->GetPtr<fidl_vector_t>(offset);
+ vector->count = 0u;
+ vector->data = reinterpret_cast<void*>(FIDL_ALLOC_ABSENT);
+}
+
+void EncodeVectorPointer(Encoder* encoder, size_t count, size_t offset) {
+ fidl_vector_t* vector = encoder->GetPtr<fidl_vector_t>(offset);
+ vector->count = count;
+ vector->data = reinterpret_cast<void*>(FIDL_ALLOC_PRESENT);
+}
+
+} // namespace fidl
diff --git a/pkg/fidl_cpp_base/decoder.cc b/pkg/fidl_cpp_base/decoder.cc
new file mode 100644
index 0000000..bec4ee0
--- /dev/null
+++ b/pkg/fidl_cpp_base/decoder.cc
@@ -0,0 +1,38 @@
+// 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/fidl/cpp/decoder.h"
+
+#include <utility>
+
+namespace fidl {
+
+Decoder::Decoder(Message message) : message_(std::move(message)) {}
+
+Decoder::~Decoder() = default;
+
+size_t Decoder::GetOffset(void* ptr) {
+ return GetOffset(reinterpret_cast<uintptr_t>(ptr));
+}
+
+size_t Decoder::GetOffset(uintptr_t ptr) {
+ // The |ptr| value comes from the message buffer, which we've already
+ // validated. That means it should coorespond to a valid offset within the
+ // message.
+ return ptr - reinterpret_cast<uintptr_t>(message_.bytes().data());
+}
+
+#ifdef __Fuchsia__
+void Decoder::DecodeHandle(zx::object_base* value, size_t offset) {
+ zx_handle_t* handle = GetPtr<zx_handle_t>(offset);
+ value->reset(*handle);
+ *handle = ZX_HANDLE_INVALID;
+}
+#endif
+
+uint8_t* Decoder::InternalGetPtr(size_t offset) {
+ return message_.bytes().data() + offset;
+}
+
+} // namespace fidl
diff --git a/pkg/fidl_cpp_base/encoder.cc b/pkg/fidl_cpp_base/encoder.cc
new file mode 100644
index 0000000..01ffb44
--- /dev/null
+++ b/pkg/fidl_cpp_base/encoder.cc
@@ -0,0 +1,60 @@
+// 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/fidl/cpp/encoder.h"
+
+#include <zircon/assert.h>
+#include <zircon/fidl.h>
+
+namespace fidl {
+namespace {
+
+size_t Align(size_t size) {
+ constexpr size_t alignment_mask = FIDL_ALIGNMENT - 1;
+ return (size + alignment_mask) & ~alignment_mask;
+}
+
+} // namespace
+
+Encoder::Encoder(uint32_t ordinal) { EncodeMessageHeader(ordinal); }
+
+Encoder::~Encoder() = default;
+
+size_t Encoder::Alloc(size_t size) {
+ size_t offset = bytes_.size();
+ size_t new_size = bytes_.size() + Align(size);
+ ZX_ASSERT(new_size >= offset);
+ bytes_.resize(new_size);
+ return offset;
+}
+
+#ifdef __Fuchsia__
+void Encoder::EncodeHandle(zx::object_base* value, size_t offset) {
+ if (value->is_valid()) {
+ *GetPtr<zx_handle_t>(offset) = FIDL_HANDLE_PRESENT;
+ handles_.push_back(value->release());
+ } else {
+ *GetPtr<zx_handle_t>(offset) = FIDL_HANDLE_ABSENT;
+ }
+}
+#endif
+
+Message Encoder::GetMessage() {
+ return Message(BytePart(bytes_.data(), bytes_.size(), bytes_.size()),
+ HandlePart(handles_.data(), handles_.size(), handles_.size()));
+}
+
+void Encoder::Reset(uint32_t ordinal) {
+ bytes_.clear();
+ handles_.clear();
+ EncodeMessageHeader(ordinal);
+}
+
+void Encoder::EncodeMessageHeader(uint32_t ordinal) {
+ size_t offset = Alloc(sizeof(fidl_message_header_t));
+ fidl_message_header_t* header = GetPtr<fidl_message_header_t>(offset);
+ header->ordinal = ordinal;
+}
+
+} // namespace fidl
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/array.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/array.h
new file mode 100644
index 0000000..aeb35e6
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/array.h
@@ -0,0 +1,98 @@
+// Copyright 2017 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.
+
+#ifndef LIB_FIDL_CPP_ARRAY_H_
+#define LIB_FIDL_CPP_ARRAY_H_
+
+#include <stddef.h>
+#include <string.h>
+
+#include <lib/fidl/cpp/comparison.h>
+
+namespace fidl {
+
+template <typename T, size_t N>
+class Array {
+ public:
+ Array() { memset(data_, 0, sizeof(data_)); }
+
+ constexpr size_t size() const { return N; }
+
+ // TODO(FIDL-245) Remove this overload.
+ constexpr size_t count() const { return N; }
+
+ const T* data() const { return data_; }
+ T* data() { return data_; }
+
+ // TODO(FIDL-245) Remove this overload.
+ T* mutable_data() { return data_; }
+
+ const T& at(size_t offset) const { return data()[offset]; }
+ T& at(size_t offset) { return mutable_data()[offset]; }
+
+ const T& operator[](size_t offset) const { return at(offset); }
+ T& operator[](size_t offset) { return at(offset); }
+
+ T* begin() { return mutable_data(); }
+ const T* begin() const { return data(); }
+ const T* cbegin() const { return data(); }
+
+ T* end() { return mutable_data() + count(); }
+ const T* end() const { return data() + count(); }
+ const T* cend() const { return data() + count(); }
+
+ private:
+ static_assert(N > 0, "fid::Array cannot have zero elements.");
+
+ T data_[N];
+};
+
+template <typename T, size_t N>
+bool operator==(const Array<T, N>& lhs, const Array<T, N>& rhs) {
+ for (size_t i = 0; i < N; ++i) {
+ if (!Equals(lhs[i], rhs[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T, size_t N>
+bool operator!=(const Array<T, N>& lhs, const Array<T, N>& rhs) {
+ return !(lhs == rhs);
+}
+
+template <typename T, size_t N>
+bool operator<(const Array<T, N>& lhs, const Array<T, N>& rhs) {
+ for (size_t i = 0; i < N; i++) {
+ if (lhs[i] != rhs[i]) {
+ return lhs[i] < rhs[i];
+ }
+ }
+ return false;
+}
+
+template <typename T, size_t N>
+bool operator>(const Array<T, N>& lhs, const Array<T, N>& rhs) {
+ for (size_t i = 0; i < N; i++) {
+ if (lhs[i] != rhs[i]) {
+ return lhs[i] > rhs[i];
+ }
+ }
+ return false;
+}
+
+template <typename T, size_t N>
+bool operator<=(const Array<T, N>& lhs, const Array<T, N>& rhs) {
+ return !(lhs > rhs);
+}
+
+template <typename T, size_t N>
+bool operator>=(const Array<T, N>& lhs, const Array<T, N>& rhs) {
+ return !(lhs < rhs);
+}
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_ARRAY_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/clone.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/clone.h
new file mode 100644
index 0000000..8c1c108
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/clone.h
@@ -0,0 +1,141 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_CLONE_H_
+#define LIB_FIDL_CPP_CLONE_H_
+
+#include <lib/fidl/cpp/array.h>
+#include <zircon/assert.h>
+#include <memory>
+#include "lib/fidl/cpp/string.h"
+#include "lib/fidl/cpp/traits.h"
+#include "lib/fidl/cpp/vector.h"
+
+namespace fidl {
+
+#ifdef __Fuchsia__
+namespace internal {
+
+template <typename T>
+inline typename std::enable_if<zx::object_traits<T>::supports_duplication,
+ zx_status_t>::type
+CloneKernelObject(const zx::object<T>& object, zx::object<T>* result) {
+ return object.duplicate(ZX_RIGHT_SAME_RIGHTS, result);
+}
+
+template <typename T>
+inline typename std::enable_if<!zx::object_traits<T>::supports_duplication,
+ zx_status_t>::type
+CloneKernelObject(const zx::object<T>& object, zx::object<T>* result) {
+ return ZX_ERR_ACCESS_DENIED;
+}
+
+} // namespace internal
+#endif // __Fuchsia__
+
+// Deep copies the contents of |value| into |result|.
+// This operation also attempts to duplicate any handles the value contains.
+//
+// Returns an error if the value could not be cloned, perhaps because a
+// handle was not duplicable.
+//
+// There are many overloads of this function with the following signature:
+// zx_status_t Clone(const T& value, T* result);
+template <typename T>
+inline typename std::enable_if<IsPrimitive<T>::value, zx_status_t>::type Clone(
+ const T& value, T* result) {
+ *result = value;
+ return ZX_OK;
+}
+
+template <typename T>
+inline
+#ifdef __Fuchsia__
+ typename std::enable_if<!IsPrimitive<T>::value &&
+ !std::is_base_of<zx::object_base, T>::value,
+ zx_status_t>::type
+#else // __Fuchsia__
+ typename std::enable_if<!IsPrimitive<T>::value, zx_status_t>::type
+#endif // __Fuchsia__
+ Clone(const T& value, T* result) {
+ return value.Clone(result);
+}
+
+#ifdef __Fuchsia__
+template <typename T>
+zx_status_t Clone(const zx::object<T>& value, zx::object<T>* result) {
+ if (!value) {
+ result->reset();
+ return ZX_OK;
+ }
+ return internal::CloneKernelObject(value, result);
+}
+#endif // __Fuchsia__
+
+template <typename T>
+inline zx_status_t Clone(const std::unique_ptr<T>& value,
+ std::unique_ptr<T>* result) {
+ if (!value) {
+ result->reset();
+ return ZX_OK;
+ }
+ *result = std::make_unique<T>();
+ return Clone(*value, result->get());
+}
+
+template <typename T>
+inline typename std::enable_if<!IsPrimitive<T>::value, zx_status_t>::type Clone(
+ const VectorPtr<T>& value, VectorPtr<T>* result) {
+ if (!value) {
+ *result = VectorPtr<T>();
+ return ZX_OK;
+ }
+ result->resize(value->size());
+ for (size_t i = 0; i < value->size(); ++i) {
+ zx_status_t status = Clone(value->at(i), &(*result)->at(i));
+ if (status != ZX_OK)
+ return status;
+ }
+ return ZX_OK;
+}
+
+template <typename T>
+inline typename std::enable_if<IsPrimitive<T>::value, zx_status_t>::type Clone(
+ const VectorPtr<T>& value, VectorPtr<T>* result) {
+ if (!value) {
+ *result = VectorPtr<T>();
+ return ZX_OK;
+ }
+ result->reset(*value);
+ return ZX_OK;
+}
+
+template <typename T, size_t N>
+inline zx_status_t Clone(const Array<T, N>& value, Array<T, N>* result) {
+ for (size_t i = 0; i < N; ++i) {
+ zx_status_t status = Clone(value[i], &result->at(i));
+ if (status != ZX_OK)
+ return status;
+ }
+ return ZX_OK;
+}
+
+zx_status_t Clone(const StringPtr& value, StringPtr* result);
+
+// Returns a deep copy of |value|.
+// This operation also attempts to duplicate any handles the value contains.
+//
+// Crashes the program if the value could not be cloned, perhaps because a
+// handle was not duplicable.
+template <typename T>
+inline T Clone(const T& value) {
+ T clone;
+ zx_status_t status = Clone(value, &clone);
+ ZX_ASSERT(status == ZX_OK);
+ return clone;
+}
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_CODING_TRAITS_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/coding_traits.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/coding_traits.h
new file mode 100644
index 0000000..0a51f5a
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/coding_traits.h
@@ -0,0 +1,165 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_CODING_TRAITS_H_
+#define LIB_FIDL_CPP_CODING_TRAITS_H_
+
+#include <lib/fidl/cpp/array.h>
+
+#include <memory>
+
+#include "lib/fidl/cpp/decoder.h"
+#include "lib/fidl/cpp/encoder.h"
+#include "lib/fidl/cpp/traits.h"
+#include "lib/fidl/cpp/vector.h"
+
+namespace fidl {
+
+template <typename T, class Enable = void>
+struct CodingTraits;
+
+template <typename T>
+struct CodingTraits<T, typename std::enable_if<IsPrimitive<T>::value>::type> {
+ static constexpr size_t encoded_size = sizeof(T);
+ inline static void Encode(Encoder* encoder, T* value, size_t offset) {
+ *encoder->GetPtr<T>(offset) = *value;
+ }
+ inline static void Decode(Decoder* decoder, T* value, size_t offset) {
+ *value = *decoder->GetPtr<T>(offset);
+ }
+};
+
+template <>
+struct CodingTraits<bool> {
+ static constexpr size_t encoded_size = sizeof(bool);
+ inline static void Encode(Encoder* encoder, bool* value, size_t offset) {
+ *encoder->GetPtr<bool>(offset) = *value;
+ }
+ inline static void Encode(Encoder* encoder, std::vector<bool>::iterator value,
+ size_t offset) {
+ *encoder->GetPtr<bool>(offset) = *value;
+ }
+ inline static void Decode(Decoder* decoder, bool* value, size_t offset) {
+ *value = *decoder->GetPtr<bool>(offset);
+ }
+ inline static void Decode(Decoder* decoder, std::vector<bool>::iterator value,
+ size_t offset) {
+ *value = *decoder->GetPtr<bool>(offset);
+ }
+};
+
+#ifdef __Fuchsia__
+template <typename T>
+struct CodingTraits<T, typename std::enable_if<
+ std::is_base_of<zx::object_base, T>::value>::type> {
+ static constexpr size_t encoded_size = sizeof(zx_handle_t);
+ static void Encode(Encoder* encoder, zx::object_base* value, size_t offset) {
+ encoder->EncodeHandle(value, offset);
+ }
+ static void Decode(Decoder* decoder, zx::object_base* value, size_t offset) {
+ decoder->DecodeHandle(value, offset);
+ }
+};
+#endif
+
+template <typename T>
+struct CodingTraits<std::unique_ptr<T>> {
+ static constexpr size_t encoded_size = sizeof(uintptr_t);
+ static void Encode(Encoder* encoder, std::unique_ptr<T>* value,
+ size_t offset) {
+ if (value->get()) {
+ *encoder->GetPtr<uintptr_t>(offset) = FIDL_ALLOC_PRESENT;
+ size_t size = CodingTraits<T>::encoded_size;
+ CodingTraits<T>::Encode(encoder, value->get(), encoder->Alloc(size));
+ } else {
+ *encoder->GetPtr<uintptr_t>(offset) = FIDL_ALLOC_ABSENT;
+ }
+ }
+ static void Decode(Decoder* decoder, std::unique_ptr<T>* value,
+ size_t offset) {
+ uintptr_t ptr = *decoder->GetPtr<uintptr_t>(offset);
+ if (!ptr)
+ return value->reset();
+ *value = std::make_unique<T>();
+ CodingTraits<T>::Decode(decoder, value->get(), decoder->GetOffset(ptr));
+ }
+};
+
+void EncodeNullVector(Encoder* encoder, size_t offset);
+void EncodeVectorPointer(Encoder* encoder, size_t count, size_t offset);
+
+template <typename T>
+struct CodingTraits<VectorPtr<T>> {
+ static constexpr size_t encoded_size = sizeof(fidl_vector_t);
+ static void Encode(Encoder* encoder, VectorPtr<T>* value, size_t offset) {
+ if (value->is_null())
+ return EncodeNullVector(encoder, offset);
+ size_t count = (*value)->size();
+ EncodeVectorPointer(encoder, count, offset);
+ size_t stride = CodingTraits<T>::encoded_size;
+ size_t base = encoder->Alloc(count * stride);
+ for (size_t i = 0; i < count; ++i)
+ CodingTraits<T>::Encode(encoder, &(*value)->at(i), base + i * stride);
+ }
+ static void Decode(Decoder* decoder, VectorPtr<T>* value, size_t offset) {
+ fidl_vector_t* encoded = decoder->GetPtr<fidl_vector_t>(offset);
+ if (!encoded->data) {
+ *value = VectorPtr<T>();
+ return;
+ }
+ value->resize(encoded->count);
+ size_t stride = CodingTraits<T>::encoded_size;
+ size_t base = decoder->GetOffset(encoded->data);
+ size_t count = encoded->count;
+ for (size_t i = 0; i < count; ++i)
+ CodingTraits<T>::Decode(decoder, &(*value)->at(i), base + i * stride);
+ }
+};
+
+template <typename T, size_t N>
+struct CodingTraits<Array<T, N>> {
+ static constexpr size_t encoded_size = CodingTraits<T>::encoded_size * N;
+ static void Encode(Encoder* encoder, Array<T, N>* value, size_t offset) {
+ size_t stride = CodingTraits<T>::encoded_size;
+ for (size_t i = 0; i < N; ++i)
+ CodingTraits<T>::Encode(encoder, &value->at(i), offset + i * stride);
+ }
+ static void Decode(Decoder* decoder, Array<T, N>* value, size_t offset) {
+ size_t stride = CodingTraits<T>::encoded_size;
+ for (size_t i = 0; i < N; ++i)
+ CodingTraits<T>::Decode(decoder, &value->at(i), offset + i * stride);
+ }
+};
+
+template <typename T, size_t EncodedSize>
+struct EncodableCodingTraits {
+ static constexpr size_t encoded_size = EncodedSize;
+ static void Encode(Encoder* encoder, T* value, size_t offset) {
+ value->Encode(encoder, offset);
+ }
+ static void Decode(Decoder* decoder, T* value, size_t offset) {
+ T::Decode(decoder, value, offset);
+ }
+};
+
+template <typename T>
+void Encode(Encoder* encoder, T* value, size_t offset) {
+ CodingTraits<T>::Encode(encoder, value, offset);
+}
+
+template <typename T>
+void Decode(Decoder* decoder, T* value, size_t offset) {
+ CodingTraits<T>::Decode(decoder, value, offset);
+}
+
+template <typename T>
+T DecodeAs(Decoder* decoder, size_t offset) {
+ T value;
+ Decode(decoder, &value, offset);
+ return value;
+}
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_CODING_TRAITS_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/comparison.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/comparison.h
new file mode 100644
index 0000000..f89f352
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/comparison.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef GARNET_PUBLIC_LIB_FIDL_CPP_COMPARISON_H_
+#define GARNET_PUBLIC_LIB_FIDL_CPP_COMPARISON_H_
+
+#include <memory>
+
+// Comparisons that uses structure equality on on std::unique_ptr instead of
+// pointer equality.
+namespace fidl {
+
+template <class T>
+inline bool Equals(const T& lhs, const T& rhs) {
+ return lhs == rhs;
+}
+
+template <class T>
+inline bool Equals(const std::unique_ptr<T>& lhs,
+ const std::unique_ptr<T>& rhs) {
+ if (lhs == nullptr || rhs == nullptr) {
+ return rhs == lhs;
+ }
+ return Equals<T>(*lhs, *rhs);
+}
+
+} // namespace fidl
+
+#endif // GARNET_PUBLIC_LIB_FIDL_CPP_COMPARISON_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/decoder.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/decoder.h
new file mode 100644
index 0000000..119dcd0
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/decoder.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_DECODER_H_
+#define LIB_FIDL_CPP_DECODER_H_
+
+#include <lib/fidl/cpp/message.h>
+#include <zircon/fidl.h>
+
+#ifdef __Fuchsia__
+#include <lib/zx/object.h>
+#endif
+
+namespace fidl {
+
+class Decoder {
+ public:
+ explicit Decoder(Message message);
+ ~Decoder();
+
+ template <typename T>
+ T* GetPtr(size_t offset) {
+ return reinterpret_cast<T*>(InternalGetPtr(offset));
+ }
+
+ size_t GetOffset(void* ptr);
+ size_t GetOffset(uintptr_t ptr);
+
+#ifdef __Fuchsia__
+ void DecodeHandle(zx::object_base* value, size_t offset);
+#endif
+
+ private:
+ uint8_t* InternalGetPtr(size_t offset);
+
+ Message message_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_DECODER_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/encoder.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/encoder.h
new file mode 100644
index 0000000..00f543e
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/encoder.h
@@ -0,0 +1,58 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_ENCODER_H_
+#define LIB_FIDL_CPP_ENCODER_H_
+
+#include <lib/fidl/cpp/message.h>
+
+#ifdef __Fuchsia__
+#include <lib/zx/object.h>
+#endif
+
+#include <zircon/fidl.h>
+
+#include <vector>
+
+namespace fidl {
+
+class Encoder {
+ public:
+ enum NoHeader { NO_HEADER };
+
+ explicit Encoder(uint32_t ordinal);
+ explicit Encoder(NoHeader) {}
+ ~Encoder();
+
+ size_t Alloc(size_t size);
+
+ template <typename T>
+ T* GetPtr(size_t offset) {
+ return reinterpret_cast<T*>(bytes_.data() + offset);
+ }
+
+#ifdef __Fuchsia__
+ void EncodeHandle(zx::object_base* value, size_t offset);
+#endif
+
+ Message GetMessage();
+
+ void Reset(uint32_t ordinal);
+
+ size_t CurrentLength() const { return bytes_.size(); }
+
+ size_t CurrentHandleCount() const { return handles_.size(); }
+
+ std::vector<uint8_t> TakeBytes() { return std::move(bytes_); }
+
+ private:
+ void EncodeMessageHeader(uint32_t ordinal);
+
+ std::vector<uint8_t> bytes_;
+ std::vector<zx_handle_t> handles_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_ENCODER_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/logging.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/logging.h
new file mode 100644
index 0000000..4ed4132
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/logging.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_LOGGING_H_
+#define LIB_FIDL_CPP_INTERNAL_LOGGING_H_
+
+#include <lib/fidl/cpp/message.h>
+
+namespace fidl {
+namespace internal {
+
+void ReportEncodingError(const Message& message, const fidl_type_t* type,
+ const char* error_msg, const char* file, int line);
+
+void ReportDecodingError(const Message& message, const fidl_type_t* type,
+ const char* error_msg, const char* file, int line);
+
+void ReportChannelWritingError(const Message& message, const fidl_type_t* type,
+ zx_status_t status, const char* file, int line);
+
+#define FIDL_REPORT_ENCODING_ERROR(message, type, error_msg) \
+ ::fidl::internal::ReportEncodingError((message), (type), (error_msg), \
+ __FILE__, __LINE__)
+
+#define FIDL_REPORT_DECODING_ERROR(message, type, error_msg) \
+ ::fidl::internal::ReportDecodingError((message), (type), (error_msg), \
+ __FILE__, __LINE__)
+
+#define FIDL_REPORT_CHANNEL_WRITING_ERROR(message, type, status) \
+ ::fidl::internal::ReportChannelWritingError((message), (type), (status), \
+ __FILE__, __LINE__)
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_LOGGING_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/object_coding.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/object_coding.h
new file mode 100644
index 0000000..202b266
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/object_coding.h
@@ -0,0 +1,45 @@
+// 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 "coding_traits.h"
+#include "encoder.h"
+
+namespace fidl {
+
+template <class T>
+zx_status_t EncodeObject(T* object, std::vector<uint8_t>* output,
+ const char** error_msg_out) {
+ Encoder encoder(Encoder::NO_HEADER);
+ object->Encode(&encoder, encoder.Alloc(CodingTraits<T>::encoded_size));
+ if (encoder.CurrentHandleCount() != 0) {
+ if (error_msg_out != nullptr) {
+ *error_msg_out = "Cannot encode handles with object encoding";
+ }
+ return ZX_ERR_INVALID_ARGS;
+ }
+ *output = encoder.TakeBytes();
+ return ZX_OK;
+}
+
+template <class T>
+zx_status_t DecodeObject(uint8_t* bytes, size_t bytes_length, T* object,
+ const char** error_msg_out) {
+ Message msg(BytePart(bytes, bytes_length, bytes_length), HandlePart());
+ zx_status_t status = msg.Decode(T::FidlType, error_msg_out);
+ if (status != ZX_OK) {
+ return status;
+ }
+ Decoder decoder(std::move(msg));
+ T::Decode(&decoder, object, 0);
+ return ZX_OK;
+}
+
+template <class T>
+zx_status_t ValidateObject(uint8_t* bytes, size_t bytes_length, T* object,
+ const char** error_msg_out) {
+ return Message(BytePart(bytes, bytes_length, bytes_length), HandlePart())
+ .Validate(T::FidlType, error_msg_out);
+}
+
+} // namespace fidl
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/string.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/string.h
new file mode 100644
index 0000000..3318bb7
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/string.h
@@ -0,0 +1,186 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_STRING_H_
+#define LIB_FIDL_CPP_STRING_H_
+
+#include <lib/fidl/cpp/builder.h>
+#include <lib/fidl/cpp/string_view.h>
+
+#include <iosfwd>
+#include <string>
+#include <utility>
+
+#include <zircon/assert.h>
+
+#include "lib/fidl/cpp/coding_traits.h"
+#include "lib/fidl/cpp/traits.h"
+
+namespace fidl {
+
+// A representation of a FIDL string that owns the memory for the string.
+//
+// A StringPtr has three states: (1) null, (2) empty, (3) contains a string. In
+// the second state, operations that return an std::string return the empty
+// std::string. The null and empty states can be distinguished using the
+// |is_null| and |operator bool| methods.
+class StringPtr {
+ public:
+ StringPtr();
+ StringPtr(const StringPtr& other);
+ StringPtr(StringPtr&& other);
+ StringPtr(std::string str);
+ StringPtr(const char* str);
+ StringPtr(const char* str, size_t length);
+ ~StringPtr();
+
+ StringPtr& operator=(const StringPtr&);
+ StringPtr& operator=(StringPtr&& other);
+
+ // Accesses the underlying std::string object.
+ const std::string& get() const { return str_; }
+
+ // Stores the given std::string in this StringPtr.
+ //
+ // After this method returns, the StringPtr is non-null.
+ void reset(std::string str) {
+ str_ = std::move(str);
+ is_null_ = false;
+ }
+
+ // Causes this StringPtr to become null.
+ void reset() {
+ str_.clear();
+ is_null_ = true;
+ }
+
+ void swap(StringPtr& other) {
+ using std::swap;
+ swap(str_, other.str_);
+ swap(is_null_, other.is_null_);
+ }
+
+ // Whether this StringPtr is null.
+ //
+ // The null state is separate from the empty state.
+ bool is_null() const { return is_null_ && str_.empty(); }
+
+ // Tests as true if non-null, false if null.
+ explicit operator bool() const { return !is_null_; }
+
+ // Provides access to the underlying std::string.
+ std::string* operator->() { return &str_; }
+ const std::string* operator->() const { return &str_; }
+
+ // Provides access to the underlying std::string.
+ const std::string& operator*() const { return str_; }
+
+ operator const std::string&() const { return str_; }
+
+ void Encode(Encoder* encoder, size_t offset);
+ static void Decode(Decoder* decoder, StringPtr* value, size_t offset);
+
+ private:
+ std::string str_;
+ bool is_null_;
+};
+
+inline bool operator==(const StringPtr& a, const StringPtr& b) {
+ if (a.is_null()) {
+ return b.is_null();
+ }
+ return !b.is_null() && a.get() == b.get();
+}
+
+inline bool operator==(const char* a, const StringPtr& b) {
+ if (a == nullptr) {
+ return b.is_null();
+ }
+ return !b.is_null() && a == b.get();
+}
+
+inline bool operator==(const StringPtr& a, const char* b) {
+ if (a.is_null()) {
+ return b == nullptr;
+ }
+ return b != nullptr && a.get() == b;
+}
+
+inline bool operator!=(const StringPtr& a, const StringPtr& b) {
+ return !(a == b);
+}
+
+inline bool operator!=(const char* a, const StringPtr& b) { return !(a == b); }
+
+inline bool operator!=(const StringPtr& a, const char* b) { return !(a == b); }
+
+inline bool operator<(const StringPtr& a, const StringPtr& b) {
+ if (a.is_null() || b.is_null()) {
+ return !b.is_null();
+ }
+ return *a < *b;
+}
+
+inline bool operator<(const char* a, const StringPtr& b) {
+ if (a == nullptr || b.is_null()) {
+ return !b.is_null();
+ }
+ return a < *b;
+}
+
+inline bool operator<(const StringPtr& a, const char* b) {
+ if (a.is_null() || b == nullptr) {
+ return b != nullptr;
+ }
+ return *a < b;
+}
+
+inline bool operator>(const StringPtr& a, const StringPtr& b) {
+ if (a.is_null() || b.is_null()) {
+ return !a.is_null();
+ }
+ return *a > *b;
+}
+
+inline bool operator>(const char* a, const StringPtr& b) {
+ if (a == nullptr || b.is_null()) {
+ return a != nullptr;
+ }
+ return a > *b;
+}
+
+inline bool operator>(const StringPtr& a, const char* b) {
+ if (a.is_null() || b == nullptr) {
+ return a != nullptr;
+ }
+ return *a > b;
+}
+
+inline bool operator<=(const StringPtr& a, const StringPtr& b) {
+ return !(a > b);
+}
+
+inline bool operator<=(const char* a, const StringPtr& b) { return !(a > b); }
+
+inline bool operator<=(const StringPtr& a, const char* b) { return !(a > b); }
+
+inline bool operator>=(const StringPtr& a, const StringPtr& b) {
+ return !(a < b);
+}
+
+inline bool operator>=(const char* a, const StringPtr& b) { return !(a < b); }
+
+inline bool operator>=(const StringPtr& a, const char* b) { return !(a < b); }
+
+inline std::ostream& operator<<(std::ostream& out, const StringPtr& str) {
+ return out << str.get();
+}
+
+template <>
+struct CodingTraits<StringPtr>
+ : public EncodableCodingTraits<StringPtr, sizeof(fidl_string_t)> {};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_STRING_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/traits.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/traits.h
new file mode 100644
index 0000000..9ce7b3a
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/traits.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_TRAITS_H_
+#define LIB_FIDL_CPP_TRAITS_H_
+
+#include <stdint.h>
+
+#include <type_traits>
+
+namespace fidl {
+
+// A type trait that indiciates whether the given type is a primitive FIDL
+// type.
+template <typename T>
+struct IsPrimitive : public std::false_type {};
+
+// clang-format off
+template <> struct IsPrimitive<bool> : public std::true_type {};
+template <> struct IsPrimitive<uint8_t> : public std::true_type {};
+template <> struct IsPrimitive<uint16_t> : public std::true_type {};
+template <> struct IsPrimitive<uint32_t> : public std::true_type {};
+template <> struct IsPrimitive<uint64_t> : public std::true_type {};
+template <> struct IsPrimitive<int8_t> : public std::true_type {};
+template <> struct IsPrimitive<int16_t> : public std::true_type {};
+template <> struct IsPrimitive<int32_t> : public std::true_type {};
+template <> struct IsPrimitive<int64_t> : public std::true_type {};
+template <> struct IsPrimitive<float> : public std::true_type {};
+template <> struct IsPrimitive<double> : public std::true_type {};
+// clang-format on
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_TRAITS_H_
diff --git a/pkg/fidl_cpp_base/include/lib/fidl/cpp/vector.h b/pkg/fidl_cpp_base/include/lib/fidl/cpp/vector.h
new file mode 100644
index 0000000..02d718c
--- /dev/null
+++ b/pkg/fidl_cpp_base/include/lib/fidl/cpp/vector.h
@@ -0,0 +1,183 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_VECTOR_H_
+#define LIB_FIDL_CPP_VECTOR_H_
+
+#include <lib/fidl/cpp/builder.h>
+#include <lib/fidl/cpp/comparison.h>
+#include <lib/fidl/cpp/vector_view.h>
+
+#include <utility>
+#include <vector>
+
+#include <zircon/assert.h>
+
+#include "lib/fidl/cpp/traits.h"
+
+namespace fidl {
+
+// A representation of a FIDL vector that owns the memory for the vector.
+//
+// A VectorPtr has three states: (1) null, (2) empty, (3) contains data. You
+// can check for the null state using the |is_null| method.
+template <typename T>
+class VectorPtr {
+ public:
+ VectorPtr() : is_null_if_empty_(true) {}
+ ~VectorPtr() = default;
+ VectorPtr(std::nullptr_t) : is_null_if_empty_(true) {}
+ explicit VectorPtr(size_t size)
+ : vec_(std::vector<T>(size)), is_null_if_empty_(false) {}
+ explicit VectorPtr(std::vector<T> vec)
+ : vec_(std::move(vec)), is_null_if_empty_(false) {}
+
+ VectorPtr(const VectorPtr&) = delete;
+ VectorPtr& operator=(const VectorPtr&) = delete;
+
+ VectorPtr(VectorPtr&& other) = default;
+ VectorPtr& operator=(VectorPtr&& other) = default;
+
+ // Creates a VectorPtr of the given size.
+ //
+ // Equivalent to using the |VectorPtr(size_t)| constructor.
+ static VectorPtr New(size_t size) { return VectorPtr(size); }
+
+ // Accesses the underlying std::vector object.
+ const std::vector<T>& get() const { return vec_; }
+
+ // Takes the std::vector from the VectorPtr.
+ //
+ // After this method returns, the VectorPtr is null.
+ std::vector<T> take() {
+ is_null_if_empty_ = true;
+ return std::move(vec_);
+ }
+
+ // Stores the given std::vector in this VectorPtr.
+ //
+ // After this method returns, the VectorPtr is non-null.
+ void reset(std::vector<T> vec) {
+ vec_ = std::move(vec);
+ is_null_if_empty_ = false;
+ }
+
+ void reset() {
+ vec_.clear();
+ is_null_if_empty_ = true;
+ }
+
+ // Resizes the underlying std::vector in this VectorPtr to the given size.
+ //
+ // After this method returns, the VectorPtr is non-null.
+ void resize(size_t size) {
+ vec_.resize(size);
+ is_null_if_empty_ = false;
+ }
+
+ // Pushes |value| onto the back of this VectorPtr.
+ //
+ // If this vector was null, it will become non-null with a size of 1.
+ void push_back(const T& value) {
+ vec_.push_back(value);
+ is_null_if_empty_ = false;
+ }
+
+ // Pushes |value| onto the back of this VectorPtr.
+ //
+ // If this vector was null, it will become non-null with a size of 1.
+ void push_back(T&& value) {
+ vec_.push_back(std::forward<T>(value));
+ is_null_if_empty_ = false;
+ }
+
+ void swap(VectorPtr& other) {
+ using std::swap;
+ swap(vec_, other.vec_);
+ swap(is_null_if_empty_, other.is_null_if_empty_);
+ }
+
+ // Returns a copy of this VectorPtr.
+ //
+ // Unlike fidl::Clone, this function can never fail. However, this function
+ // works only if T is copiable.
+ VectorPtr Clone() const {
+ if (is_null())
+ return VectorPtr();
+ return VectorPtr(vec_);
+ }
+
+ // Whether this VectorPtr is null.
+ //
+ // The null state is separate from the empty state.
+ bool is_null() const { return is_null_if_empty_ && vec_.empty(); }
+
+ // Tests as true if non-null, false if null.
+ explicit operator bool() const { return !is_null(); }
+
+ // Provides access to the underlying std::vector.
+ std::vector<T>* operator->() { return &vec_; }
+ const std::vector<T>* operator->() const { return &vec_; }
+
+ // Provides access to the underlying std::vector.
+ std::vector<T>& operator*() { return vec_; }
+ const std::vector<T>& operator*() const { return vec_; }
+
+ operator const std::vector<T>&() const { return vec_; }
+
+ private:
+ std::vector<T> vec_;
+ bool is_null_if_empty_;
+};
+
+template <class T>
+inline bool operator==(const VectorPtr<T>& lhs, const VectorPtr<T>& rhs) {
+ if (lhs.is_null() || rhs.is_null()) {
+ return lhs.is_null() == rhs.is_null();
+ }
+ if (lhs->size() != rhs->size()) {
+ return false;
+ }
+ for (size_t i = 0; i < lhs->size(); ++i) {
+ if (!Equals(lhs->at(i), rhs->at(i))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <class T>
+inline bool operator!=(const VectorPtr<T>& lhs, const VectorPtr<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+template <class T>
+inline bool operator<(const VectorPtr<T>& lhs, const VectorPtr<T>& rhs) {
+ if (lhs.is_null() || rhs.is_null()) {
+ return !rhs.is_null();
+ }
+ return *lhs < *rhs;
+}
+
+template <class T>
+inline bool operator>(const VectorPtr<T>& lhs, const VectorPtr<T>& rhs) {
+ if (lhs.is_null() || rhs.is_null()) {
+ return !lhs.is_null();
+ }
+ return *lhs > *rhs;
+}
+
+template <class T>
+inline bool operator<=(const VectorPtr<T>& lhs, const VectorPtr<T>& rhs) {
+ return !(lhs > rhs);
+}
+
+template <class T>
+inline bool operator>=(const VectorPtr<T>& lhs, const VectorPtr<T>& rhs) {
+ return !(lhs < rhs);
+}
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_VECTOR_H_
diff --git a/pkg/fidl_cpp_base/internal/logging.cc b/pkg/fidl_cpp_base/internal/logging.cc
new file mode 100644
index 0000000..c9fd77d
--- /dev/null
+++ b/pkg/fidl_cpp_base/internal/logging.cc
@@ -0,0 +1,51 @@
+// 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/fidl/cpp/internal/logging.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace fidl {
+namespace internal {
+
+void ReportEncodingError(const Message& message, const fidl_type_t* type,
+ const char* error_msg, const char* file, int line) {
+ char type_name[1024];
+ size_t type_name_length =
+ fidl_format_type_name(type, type_name, sizeof(type_name));
+ fprintf(stderr,
+ "fidl encoding error at %s:%d: %s, "
+ "type %.*s, %" PRIu32 " bytes, %" PRIu32 " handles\n",
+ file, line, error_msg, static_cast<int>(type_name_length), type_name,
+ message.bytes().actual(), message.handles().actual());
+}
+
+void ReportDecodingError(const Message& message, const fidl_type_t* type,
+ const char* error_msg, const char* file, int line) {
+ char type_name[1024];
+ size_t type_name_length =
+ fidl_format_type_name(type, type_name, sizeof(type_name));
+ fprintf(stderr,
+ "fidl decoding error at %s:%d: %s, "
+ "type %.*s, %" PRIu32 " bytes, %" PRIu32 " handles\n",
+ file, line, error_msg, static_cast<int>(type_name_length), type_name,
+ message.bytes().actual(), message.handles().actual());
+}
+
+void ReportChannelWritingError(const Message& message, const fidl_type_t* type,
+ zx_status_t status, const char* file, int line) {
+ char type_name[1024];
+ size_t type_name_length =
+ fidl_format_type_name(type, type_name, sizeof(type_name));
+ fprintf(stderr,
+ "fidl channel writing error at %s:%d: zx_status_t %d, "
+ "type %.*s, %" PRIu32 " bytes, %" PRIu32 " handles\n",
+ file, line, status, static_cast<int>(type_name_length), type_name,
+ message.bytes().actual(), message.handles().actual());
+}
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp_base/meta.json b/pkg/fidl_cpp_base/meta.json
new file mode 100644
index 0000000..29eb264
--- /dev/null
+++ b/pkg/fidl_cpp_base/meta.json
@@ -0,0 +1,34 @@
+{
+ "deps": [
+ "fit",
+ "fidl",
+ "fidl-async",
+ "zx"
+ ],
+ "fidl_deps": [],
+ "headers": [
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/array.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/clone.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/coding_traits.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/comparison.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/decoder.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/encoder.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/internal/logging.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/object_coding.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/string.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/traits.h",
+ "pkg/fidl_cpp_base/include/lib/fidl/cpp/vector.h"
+ ],
+ "include_dir": "pkg/fidl_cpp_base/include",
+ "name": "fidl_cpp_base",
+ "root": "pkg/fidl_cpp_base",
+ "sources": [
+ "pkg/fidl_cpp_base/clone.cc",
+ "pkg/fidl_cpp_base/coding_traits.cc",
+ "pkg/fidl_cpp_base/decoder.cc",
+ "pkg/fidl_cpp_base/encoder.cc",
+ "pkg/fidl_cpp_base/internal/logging.cc",
+ "pkg/fidl_cpp_base/string.cc"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/fidl_cpp_base/string.cc b/pkg/fidl_cpp_base/string.cc
new file mode 100644
index 0000000..35b8f46
--- /dev/null
+++ b/pkg/fidl_cpp_base/string.cc
@@ -0,0 +1,61 @@
+// 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/fidl/cpp/string.h"
+
+#include <string.h>
+
+#include "lib/fidl/cpp/encoder.h"
+
+namespace fidl {
+
+StringPtr::StringPtr() : is_null_(true) {}
+
+StringPtr::StringPtr(const StringPtr& other) = default;
+
+StringPtr::StringPtr(std::string str) : str_(std::move(str)), is_null_(false) {}
+
+StringPtr::StringPtr(const char* str)
+ : str_(str ? std::string(str) : std::string()), is_null_(!str) {}
+
+StringPtr::StringPtr(const char* str, size_t length)
+ : str_(str ? std::string(str, length) : std::string()), is_null_(!str) {}
+
+StringPtr::~StringPtr() = default;
+
+StringPtr::StringPtr(StringPtr&& other)
+ : str_(std::move(other.str_)), is_null_(other.is_null_) {}
+
+StringPtr& StringPtr::operator=(const StringPtr& other) = default;
+
+StringPtr& StringPtr::operator=(StringPtr&& other) {
+ str_ = std::move(other.str_);
+ is_null_ = other.is_null_;
+ return *this;
+}
+
+void StringPtr::Encode(Encoder* encoder, size_t offset) {
+ fidl_string_t* string = encoder->GetPtr<fidl_string_t>(offset);
+ if (is_null()) {
+ string->size = 0u;
+ string->data = reinterpret_cast<char*>(FIDL_ALLOC_ABSENT);
+ } else {
+ string->size = str_.size();
+ string->data = reinterpret_cast<char*>(FIDL_ALLOC_PRESENT);
+ size_t base = encoder->Alloc(str_.size());
+ char* payload = encoder->GetPtr<char>(base);
+ memcpy(payload, str_.data(), str_.size());
+ }
+}
+
+void StringPtr::Decode(Decoder* decoder, StringPtr* value, size_t offset) {
+ fidl_string_t* string = decoder->GetPtr<fidl_string_t>(offset);
+ if (string->data) {
+ value->reset(std::string(string->data, string->size));
+ } else {
+ *value = StringPtr();
+ }
+}
+
+} // namespace fidl
diff --git a/pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_handle.h b/pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_handle.h
new file mode 100644
index 0000000..2257a00
--- /dev/null
+++ b/pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_handle.h
@@ -0,0 +1,186 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERFACE_HANDLE_H_
+#define LIB_FIDL_CPP_INTERFACE_HANDLE_H_
+
+#include <lib/zx/channel.h>
+#include <zircon/assert.h>
+
+#include <cstddef>
+#include <utility>
+
+#include "lib/fidl/cpp/clone.h"
+#include "lib/fidl/cpp/coding_traits.h"
+#include "lib/fidl/cpp/interface_request.h"
+
+namespace fidl {
+class Builder;
+template <typename Interface>
+class InterfacePtr;
+template <typename Interface>
+class SynchronousInterfacePtr;
+
+// The client endpoint of a FIDL channel.
+//
+// The remote end of the channel expects this end of the channel to speak the
+// protocol associated with |Interface|. This type is the dual of
+// |InterfaceRequest|.
+//
+// Unlike an |InterfacePtr|, an |InterfaceHandle| does not have thread affinity
+// and can therefore be transferred to another thread or another process. To
+// create an |InterfacePtr| to send messages on this channel, call the |Bind()|
+// method, either on the |InterfaceHandle| or the |InterfacePtr| object.
+//
+// See also:
+//
+// * |InterfaceRequest|, which is the server analog of an |InterfaceHandle|.
+template <typename Interface>
+class InterfaceHandle {
+ public:
+ // Creates an |InterfaceHandle| whose underlying channel is invalid.
+ InterfaceHandle() = default;
+
+ // Creates an |InterfaceHandle| that wraps the given |channel|.
+ explicit InterfaceHandle(zx::channel channel)
+ : channel_(std::move(channel)) {}
+
+ InterfaceHandle(const InterfaceHandle& other) = delete;
+ InterfaceHandle& operator=(const InterfaceHandle& other) = delete;
+
+ InterfaceHandle(InterfaceHandle&& other)
+ : channel_(std::move(other.channel_)) {}
+
+ InterfaceHandle& operator=(InterfaceHandle&& other) {
+ channel_ = std::move(other.channel_);
+ return *this;
+ }
+
+ // Implicit conversion from nullptr to an |InterfaceHandle| without a valid
+ // |channel|.
+ InterfaceHandle(std::nullptr_t) {}
+
+ // Implicit conversion from |InterfacePtr| unbinds the channel from the
+ // |InterfacePtr|.
+ //
+ // Making this constructor templated ensures that it is not type-instantiated
+ // unless it is used, making the InterfacePtr<->InterfaceHandle codependency
+ // less fragile.
+ template <typename InterfacePtr = InterfacePtr<Interface>>
+ InterfaceHandle(InterfacePtr&& ptr) {
+ *this = ptr.Unbind();
+ }
+
+ // Creates a new channel, retains one endpoint in this |InterfaceHandle| and
+ // returns the other as an |InterfaceRequest|.
+ //
+ // Typically, the returned |InterfaceRequest| is passed to another process,
+ // which will implement the server endpoint for the |Interface| protocol.
+ //
+ // If |NewRequest| fails to create the underlying channel, the returned
+ // |InterfaceRequest| will return false from |is_valid()|.
+ InterfaceRequest<Interface> NewRequest() {
+ zx::channel h1, h2;
+ if (zx::channel::create(0, &h1, &h2) != ZX_OK)
+ return nullptr;
+ channel_ = std::move(h1);
+ return InterfaceRequest<Interface>(std::move(h2));
+ }
+
+ // Creates an |InterfacePtr| bound to the channel in this |InterfaceHandle|.
+ //
+ // This function transfers ownership of the underlying channel to the
+ // returned |InterfacePtr|, which means the |is_valid()| method will return
+ // false after this method returns.
+ //
+ // Requires the current thread to have a default async_t (e.g., a message
+ // loop) in order to read messages from the channel and to monitor the
+ // channel for |ZX_CHANNEL_PEER_CLOSED|.
+ //
+ // Making this method templated ensures that it is not type-instantiated
+ // unless it is used, making the InterfacePtr<->InterfaceHandle codependency
+ // less fragile.
+ template <typename InterfacePtr = InterfacePtr<Interface>>
+ inline InterfacePtr Bind() {
+ InterfacePtr ptr;
+ ptr.Bind(std::move(channel_));
+ return ptr;
+ }
+
+ template <typename SyncInterfacePtr = SynchronousInterfacePtr<Interface>>
+ inline SyncInterfacePtr BindSync() {
+ SyncInterfacePtr ptr;
+ ptr.Bind(std::move(channel_));
+ return ptr;
+ }
+
+ // Whether the underlying channel is valid.
+ bool is_valid() const { return !!channel_; }
+ explicit operator bool() const { return is_valid(); }
+
+ // Transfers ownership of the underlying channel to the caller.
+ zx::channel TakeChannel() { return std::move(channel_); }
+
+ // The underlying channel.
+ const zx::channel& channel() const { return channel_; }
+ void set_channel(zx::channel channel) { channel_ = std::move(channel); }
+
+ void Encode(Encoder* encoder, size_t offset) {
+ encoder->EncodeHandle(&channel_, offset);
+ }
+
+ static void Decode(Decoder* decoder, InterfaceHandle<Interface>* value,
+ size_t offset) {
+ decoder->DecodeHandle(&value->channel_, offset);
+ }
+
+ private:
+ zx::channel channel_;
+};
+
+// Equality.
+template <typename T>
+bool operator==(const InterfaceHandle<T>& lhs, const InterfaceHandle<T>& rhs) {
+ return lhs.channel() == rhs.channel();
+}
+template <typename T>
+bool operator!=(const InterfaceHandle<T>& lhs, const InterfaceHandle<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+// Comparisons.
+template <typename T>
+bool operator<(const InterfaceHandle<T>& lhs, const InterfaceHandle<T>& rhs) {
+ return lhs.channel() < rhs.channel();
+}
+template <typename T>
+bool operator>(const InterfaceHandle<T>& lhs, const InterfaceHandle<T>& rhs) {
+ return lhs.channel() > rhs.channel();
+}
+template <typename T>
+bool operator<=(const InterfaceHandle<T>& lhs, const InterfaceHandle<T>& rhs) {
+ return !(lhs > rhs);
+}
+template <typename T>
+bool operator>=(const InterfaceHandle<T>& lhs, const InterfaceHandle<T>& rhs) {
+ return !(lhs < rhs);
+}
+
+template <typename T>
+struct CodingTraits<InterfaceHandle<T>>
+ : public EncodableCodingTraits<InterfaceHandle<T>, sizeof(zx_handle_t)> {};
+
+template <typename T>
+inline zx_status_t Clone(const InterfaceHandle<T>& value,
+ InterfaceHandle<T>* result) {
+ if (!value) {
+ *result = InterfaceHandle<T>();
+ return ZX_OK;
+ }
+ return ZX_ERR_ACCESS_DENIED;
+}
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERFACE_HANDLE_H_
diff --git a/pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_request.h b/pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_request.h
new file mode 100644
index 0000000..45af19c
--- /dev/null
+++ b/pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_request.h
@@ -0,0 +1,171 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERFACE_REQUEST_H_
+#define LIB_FIDL_CPP_INTERFACE_REQUEST_H_
+
+#include <lib/fit/function.h>
+#include <lib/zx/channel.h>
+
+#include <cstddef>
+#include <utility>
+
+#include "lib/fidl/cpp/clone.h"
+#include "lib/fidl/cpp/coding_traits.h"
+
+namespace fidl {
+class Builder;
+
+// The server endpoint of a FIDL channel.
+//
+// The remote end of the channel expects this end of the channel to speak the
+// protocol associated with |Interface|. This type is the dual of
+// |InterfaceHandle|.
+//
+// An |InterfaceRequest| does not have thread affinity and can therefore be
+// transferred to another thread or another process. To bind an implementation
+// of |Interface| to this |InterfaceRequest|, use a |Binding| object.
+//
+// Typically, |InterfaceRequest| objects are created by a prospective client of
+// |Interface|, which then sends the |InterfaceRequest| to another process to
+// request that the remote process implement the |Interface|. This pattern
+// enables *pipelined* operation, in which the client can start calling methods
+// on an associated |InterfacePtr| immediately, before the |InterfaceRequest|
+// has reached the remote process and been bound to an implementation. These
+// method calls are buffered by the underlying channel until they are read by
+// the remote process.
+//
+// Example:
+//
+// #include "foo.fidl.h"
+//
+// class FooImpl : public Foo {
+// public:
+// explicit FooImpl(InterfaceRequest<Foo> request)
+// : binding_(this, std::move(request)) {}
+//
+// // Foo implementation here.
+//
+// private:
+// Binding<Foo> binding_;
+// };
+//
+// After the |InterfaceRequest| has been bound to an implementation, the
+// implementation will receive method calls from the remote endpoint of the
+// channel on the thread on which the |InterfaceRequest| was bound.
+//
+// See also:
+//
+// * |InterfaceHandle|, which is the client analog of an |InterfaceRequest|.
+template <typename Interface>
+class InterfaceRequest {
+ public:
+ // Creates an |InterfaceHandle| whose underlying channel is invalid.
+ //
+ // Some protocols contain messages that permit such |InterfaceRequest|
+ // objects, which indicate that the client is not interested in the server
+ // providing an implementation of |Interface|.
+ InterfaceRequest() = default;
+
+ // Creates an |InterfaceHandle| that wraps the given |channel|.
+ explicit InterfaceRequest(zx::channel channel)
+ : channel_(std::move(channel)) {}
+
+ InterfaceRequest(const InterfaceRequest& other) = delete;
+ InterfaceRequest& operator=(const InterfaceRequest& other) = delete;
+
+ InterfaceRequest(InterfaceRequest&& other)
+ : channel_(std::move(other.channel_)) {}
+
+ InterfaceRequest& operator=(InterfaceRequest&& other) {
+ channel_ = std::move(other.channel_);
+ return *this;
+ }
+
+ // Implicit conversion from nullptr to an |InterfaceRequest| without an
+ // invalid |channel|.
+ InterfaceRequest(std::nullptr_t) {}
+
+ // Whether the underlying channel is valid.
+ bool is_valid() const { return !!channel_; }
+ explicit operator bool() const { return is_valid(); }
+
+ // Transfers ownership of the underlying channel to the caller.
+ zx::channel TakeChannel() { return std::move(channel_); }
+
+ // The underlying channel.
+ const zx::channel& channel() const { return channel_; }
+ void set_channel(zx::channel channel) { channel_ = std::move(channel); }
+
+ void Encode(Encoder* encoder, size_t offset) {
+ encoder->EncodeHandle(&channel_, offset);
+ }
+
+ static void Decode(Decoder* decoder, InterfaceRequest<Interface>* value,
+ size_t offset) {
+ decoder->DecodeHandle(&value->channel_, offset);
+ }
+
+ private:
+ zx::channel channel_;
+};
+
+// A |InterfaceRequestHandler<Interface>| is simply a function that
+// handles an interface request for |Interface|. If it determines that the
+// request should be "accepted", then it should "connect" ("take ownership
+// of") request. Otherwise, it can simply drop |request| (as implied by the
+// interface).
+template <typename Interface>
+using InterfaceRequestHandler =
+ fit::function<void(fidl::InterfaceRequest<Interface> request)>;
+
+// Equality.
+template <typename T>
+bool operator==(const InterfaceRequest<T>& lhs,
+ const InterfaceRequest<T>& rhs) {
+ return lhs.channel() == rhs.channel();
+}
+template <typename T>
+bool operator!=(const InterfaceRequest<T>& lhs,
+ const InterfaceRequest<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+// Comparaisons.
+template <typename T>
+bool operator<(const InterfaceRequest<T>& lhs, const InterfaceRequest<T>& rhs) {
+ return lhs.channel() < rhs.channel();
+}
+template <typename T>
+bool operator>(const InterfaceRequest<T>& lhs, const InterfaceRequest<T>& rhs) {
+ return lhs.channel() > rhs.channel();
+}
+template <typename T>
+bool operator<=(const InterfaceRequest<T>& lhs,
+ const InterfaceRequest<T>& rhs) {
+ return !(lhs > rhs);
+}
+template <typename T>
+bool operator>=(const InterfaceRequest<T>& lhs,
+ const InterfaceRequest<T>& rhs) {
+ return !(lhs < rhs);
+}
+
+template <typename T>
+struct CodingTraits<InterfaceRequest<T>>
+ : public EncodableCodingTraits<InterfaceRequest<T>, sizeof(zx_handle_t)> {};
+
+template <typename T>
+inline zx_status_t Clone(const InterfaceRequest<T>& value,
+ InterfaceRequest<T>* result) {
+ if (!value) {
+ *result = InterfaceRequest<T>();
+ return ZX_OK;
+ }
+ return ZX_ERR_ACCESS_DENIED;
+}
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERFACE_REQUEST_H_
diff --git a/pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/synchronous_proxy.h b/pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/synchronous_proxy.h
new file mode 100644
index 0000000..ee77c99
--- /dev/null
+++ b/pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/synchronous_proxy.h
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_INTERNAL_SYNCHRONOUS_PROXY_H_
+#define LIB_FIDL_CPP_INTERNAL_SYNCHRONOUS_PROXY_H_
+
+#include <lib/fidl/cpp/message.h>
+#include <lib/zx/channel.h>
+#include <zircon/fidl.h>
+
+namespace fidl {
+namespace internal {
+
+// Manages the client state for a synchronous interface.
+//
+// A |SynchronousProxy| manages the client state for a sychronous interface.
+// This object validates messages before sending them to the remote endpoint,
+// and (optionally) blocks until it receives a reply.
+//
+// This object is thread-safe.
+class SynchronousProxy {
+ public:
+ // Creates a |SynchronousProxy| that wraps the given channel.
+ explicit SynchronousProxy(zx::channel channel);
+ ~SynchronousProxy();
+
+ // Returns the underlying channel from this object.
+ //
+ // The |SynchronousProxy| does not attempt to synchronize this operation with
+ // |Send| or |Call|.
+ zx::channel TakeChannel();
+
+ // Validates that |message| matches the given |type| and sends the message
+ // through the underlying channel.
+ //
+ // Does not block.
+ //
+ // Returns an error if validation or writing fails.
+ zx_status_t Send(const fidl_type_t* type, Message message);
+
+ // Validate that |request| matches the given |request_type| and sends
+ // |request| through the underlying channel. Blocks until it receives a
+ // response, which is then decoded according to |response_type| and returned
+ // in |response_type|.
+ //
+ // Blocks until the remote endpoint replied.
+ //
+ // Returns an error if validation, writing, reading, or decoding fails.
+ zx_status_t Call(const fidl_type_t* request_type,
+ const fidl_type_t* response_type, Message request,
+ Message* response);
+
+ private:
+ zx::channel channel_;
+};
+
+} // namespace internal
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_INTERNAL_SYNCHRONOUS_PROXY_H_
diff --git a/pkg/fidl_cpp_sync/include/lib/fidl/cpp/synchronous_interface_ptr.h b/pkg/fidl_cpp_sync/include/lib/fidl/cpp/synchronous_interface_ptr.h
new file mode 100644
index 0000000..7f9ff6c
--- /dev/null
+++ b/pkg/fidl_cpp_sync/include/lib/fidl/cpp/synchronous_interface_ptr.h
@@ -0,0 +1,185 @@
+// 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.
+
+#ifndef LIB_FIDL_CPP_SYNCHRONOUS_INTERFACE_PTR_H_
+#define LIB_FIDL_CPP_SYNCHRONOUS_INTERFACE_PTR_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "lib/fidl/cpp/interface_handle.h"
+
+namespace fidl {
+
+// A synchronous client interface to a remote implementation of |Interface|.
+//
+// An |SynchronousInterfacePtr| implements |Interface| by proxying calls through
+// a |channel| to a remote implementation of |Interface|. Method calls on the
+// |Interface| proxy are encoded and sent through the bound channel to the
+// remote endpoint, which processes them. If the method has a reply (including
+// any empty reply), the client blocks and waits for the remote endpoint to
+// reply.
+//
+// You need to bind the |SynchronousInterfacePtr| before calling any |Interface|
+// methods. There are a number of ways to bind the |SynchronousInterfacePtr|.
+// See |NewRequest|, |Bind|, and the |BindSync| method on |InterfaceHandle|.
+//
+// This class is thread-compatible. Once bound, the |SynchronousInterfacePtr|
+// can be used from multiple threads simultaneously. However, the
+// |SynchronousInterfacePtr| does not attempt to synchronize mutating operatios,
+// such as |Bind| or |Unbind|.
+//
+// |SynchronousInterfacePtr| does not require a |async_t| implementation and
+// does not bind to the default |async_dispatcher_t*| for the current thread,
+// unlike |InterfacePtr|.
+//
+// See also:
+//
+// * |Binding|, which is the server analog of an |SynchronousInterfacePtr|.
+// * |InterfacePtr|, which is an asynchronous interface to a remote
+// implementation.
+template <typename Interface>
+class SynchronousInterfacePtr {
+ public:
+ using InterfaceSync = typename Interface::Sync_;
+
+ // Creates an unbound |SynchronousInterfacePtr|.
+ SynchronousInterfacePtr() {}
+ SynchronousInterfacePtr(std::nullptr_t) {}
+
+ SynchronousInterfacePtr(const SynchronousInterfacePtr& other) = delete;
+ SynchronousInterfacePtr& operator=(const SynchronousInterfacePtr& other) =
+ delete;
+
+ SynchronousInterfacePtr(SynchronousInterfacePtr&& other) = default;
+ SynchronousInterfacePtr& operator=(SynchronousInterfacePtr&& other) = default;
+
+ // Bind the |SynchronousInterfacePtr| to one endpoint of a newly created
+ // channel and return the other endpoint as an |InterfaceRequest|.
+ //
+ // Typically, the returned |InterfaceRequest| will be sent to a remote process
+ // to be bound to an implementation of |Interface| using a |Binding| object.
+ //
+ // After calling this method, clients can start calling methods on this
+ // |SynchronousInterfacePtr|. However, methods that have replies will block
+ // until the remote implementation binds the |InterfaceRequest| and replies.
+ //
+ // # Example
+ //
+ // Given the following interface:
+ //
+ // interface Database {
+ // OpenTable(request<Table> table);
+ // };
+ //
+ // The client can use the |NewRequest| method to create the |InterfaceRequest|
+ // object needed by the |OpenTable| method:
+ //
+ // DatabasePtr database = ...; // Connect to database.
+ // TableSyncPtr table;
+ // database->OpenTable(table.NewRequest());
+ //
+ // The client can call methods on |table| immediately. Messages that have
+ // replies will block until the Database implementation binds a Table
+ // implementation and replies.
+ InterfaceRequest<Interface> NewRequest() {
+ zx::channel h1;
+ zx::channel h2;
+ if (zx::channel::create(0, &h1, &h2) != ZX_OK)
+ return nullptr;
+ Bind(std::move(h1));
+ return InterfaceRequest<Interface>(std::move(h2));
+ }
+
+ // Binds the |SynchronousInterfacePtr| to the given |channel|.
+ //
+ // The |SynchronousInterfacePtr| expects the remote end of the |channel| to
+ // speak the protocol defined by |Interface|. Unlike the |Bind| overload that
+ // takes a |InterfaceHandle| parameter, this |Bind| overload lacks type
+ // safety.
+ //
+ // If the |SynchronousInterfacePtr| was prevously bound to another channel,
+ // that channel is closed. If the |channel| is invalid, then this method will
+ // effectively unbind the |SynchronousInterfacePtr|. A more direct way to have
+ // that effect is to call |Unbind|.
+ //
+ // Does not require the current thread to have a default async_t.
+ void Bind(zx::channel channel) {
+ if (!channel) {
+ proxy_.reset();
+ return;
+ }
+ proxy_.reset(new typename InterfaceSync::Proxy_(std::move(channel)));
+ }
+
+ // Binds the |SynchronousInterfacePtr| to the given |InterfaceHandle|.
+ //
+ // The |SynchronousInterfacePtr| expects the remote end of the |channel| to
+ // speak the protocol defined by |Interface|. Unlike the |Bind| overload that
+ // takes a |channel| parameter, this |Bind| overload provides type safety.
+ //
+ // If the |SynchronousInterfacePtr| was prevously bound to another channel,
+ // that channel is closed. If the |InterfaceHandle| is invalid, then this
+ // method will effectively unbind the |SynchronousInterfacePtr|. A more direct
+ // way to have that effect is to call |Unbind|.
+ //
+ // Does not require the current thread to have a default async_t.
+ void Bind(InterfaceHandle<Interface> handle) {
+ return Bind(handle.TakeChannel());
+ }
+
+ // Unbinds the underlying channel from the |SynchronousInterfacePtr|.
+ //
+ // The underlying channel is returned as an |InterfaceHandle|, which is safe
+ // to transport to another thread or process.
+ //
+ // After this method returns, a subsequent call to |Bind| is required before
+ // calling any additional |Interface| methods.
+ InterfaceHandle<Interface> Unbind() {
+ InterfaceHandle<Interface> handle(proxy_->proxy().TakeChannel());
+ proxy_.reset();
+ return handle;
+ }
+
+ // Whether this |SynchronousInterfacePtr| is currently bound to a channel.
+ //
+ // If the |SynchronousInterfacePtr| is bound to a channel, calls to
+ // |Interface| methods are proxied to the remote endpoint of the channel.
+ //
+ // See also:
+ //
+ // * |Bind|, which binds a channel to this |SynchronousInterfacePtr|.
+ // * |Unbind|, which unbinds a channel from this |SynchronousInterfacePtr|.
+ bool is_bound() const { return static_cast<bool>(proxy_); }
+
+ // Whether this |SynchronousInterfacePtr| is currently bound to a channel.
+ //
+ // See |is_bound| for details.
+ explicit operator bool() const { return is_bound(); }
+
+ // The |Interface| proxy associated with this |SynchronousInterfacePtr|.
+ //
+ // When this |SynchronousInterfacePtr| is bound, method calls on this
+ // |Interface| will be proxied to the remote endpoint of the connection.
+ // Methods that expect replies will block until the
+ // |SynchronousInterfacePtr| either receives a reply to that transaction.
+ //
+ // When this |SynchronousInterfacePtr| is not bound, this method returns
+ // nullptr.
+ //
+ // The returned |Interface| is thread-compatible and can be used from any
+ // thread.
+ InterfaceSync* get() const { return proxy_.get(); }
+ InterfaceSync* operator->() const { return get(); }
+ InterfaceSync& operator*() const { return *get(); }
+
+ private:
+ std::unique_ptr<typename InterfaceSync::Proxy_> proxy_;
+};
+
+} // namespace fidl
+
+#endif // LIB_FIDL_CPP_SYNCHRONOUS_INTERFACE_PTR_H_
diff --git a/pkg/fidl_cpp_sync/internal/synchronous_proxy.cc b/pkg/fidl_cpp_sync/internal/synchronous_proxy.cc
new file mode 100644
index 0000000..0e42e56
--- /dev/null
+++ b/pkg/fidl_cpp_sync/internal/synchronous_proxy.cc
@@ -0,0 +1,53 @@
+// 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/fidl/cpp/internal/synchronous_proxy.h"
+
+#include <memory>
+#include <utility>
+
+#include "lib/fidl/cpp/internal/logging.h"
+
+namespace fidl {
+namespace internal {
+
+SynchronousProxy::SynchronousProxy(zx::channel channel)
+ : channel_(std::move(channel)) {}
+
+SynchronousProxy::~SynchronousProxy() = default;
+
+zx::channel SynchronousProxy::TakeChannel() { return std::move(channel_); }
+
+zx_status_t SynchronousProxy::Send(const fidl_type_t* type, Message message) {
+ const char* error_msg = nullptr;
+ zx_status_t status = message.Validate(type, &error_msg);
+ if (status != ZX_OK) {
+ FIDL_REPORT_ENCODING_ERROR(message, type, error_msg);
+ return status;
+ }
+ return message.Write(channel_.get(), 0);
+}
+
+zx_status_t SynchronousProxy::Call(const fidl_type_t* request_type,
+ const fidl_type_t* response_type,
+ Message request, Message* response) {
+ const char* error_msg = nullptr;
+ zx_status_t status = request.Validate(request_type, &error_msg);
+ if (status != ZX_OK) {
+ FIDL_REPORT_ENCODING_ERROR(request, request_type, error_msg);
+ return status;
+ }
+ status = request.Call(channel_.get(), 0, ZX_TIME_INFINITE, response);
+ if (status != ZX_OK)
+ return status;
+ status = response->Decode(response_type, &error_msg);
+ if (status != ZX_OK) {
+ FIDL_REPORT_DECODING_ERROR(*response, response_type, error_msg);
+ return status;
+ }
+ return ZX_OK;
+}
+
+} // namespace internal
+} // namespace fidl
diff --git a/pkg/fidl_cpp_sync/meta.json b/pkg/fidl_cpp_sync/meta.json
new file mode 100644
index 0000000..c74ae9d
--- /dev/null
+++ b/pkg/fidl_cpp_sync/meta.json
@@ -0,0 +1,23 @@
+{
+ "deps": [
+ "fidl_cpp_base",
+ "fidl",
+ "fidl-async",
+ "fit",
+ "zx"
+ ],
+ "fidl_deps": [],
+ "headers": [
+ "pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_handle.h",
+ "pkg/fidl_cpp_sync/include/lib/fidl/cpp/interface_request.h",
+ "pkg/fidl_cpp_sync/include/lib/fidl/cpp/internal/synchronous_proxy.h",
+ "pkg/fidl_cpp_sync/include/lib/fidl/cpp/synchronous_interface_ptr.h"
+ ],
+ "include_dir": "pkg/fidl_cpp_sync/include",
+ "name": "fidl_cpp_sync",
+ "root": "pkg/fidl_cpp_sync",
+ "sources": [
+ "pkg/fidl_cpp_sync/internal/synchronous_proxy.cc"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/fit/include/lib/fit/bridge.h b/pkg/fit/include/lib/fit/bridge.h
new file mode 100644
index 0000000..3fed908
--- /dev/null
+++ b/pkg/fit/include/lib/fit/bridge.h
@@ -0,0 +1,488 @@
+// 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.
+
+#ifndef LIB_FIT_BRIDGE_H_
+#define LIB_FIT_BRIDGE_H_
+
+#include "bridge_internal.h"
+
+namespace fit {
+
+// A bridge is a building block for asynchronous control flow that is formed
+// by the association of two distinct participants: a completer and a consumer.
+//
+// - The completer is responsible for reporting completion of an asynchronous
+// task and providing its result. See |completer()| and |fit::completer|.
+// - The consumer is responsible for consuming the result of the asynchronous
+// task. See |consumer()| and |fit::consumer|.
+//
+// This class is often used for binding a |fit::promise| to a callback,
+// facilitating interoperation of promises with functions that asynchronously
+// report their result via a callback function. It can also be used more
+// generally anytime it is necessary to decouple completion of an asynchronous
+// task from consumption of its result (possibly on different threads).
+//
+// The completer and consumer each possesses a unique capability that can
+// be exercised at most once during their association: the asynchronous
+// task represented by a bridge can be completed at most once and its
+// result can be consumed at most once. This property is enforced by
+// a single-ownership model for completers and consumers.
+//
+// The completion capability has a single owner represented by |fit::completer|.
+// Its owner may exercise the capability to complete the task (provide its result),
+// it may transfer the capability by moving it to another completer instance,
+// or it may cause the asynchronous task to be "abandoned" by discarding the
+// capability, implying that the task can never produce a result. When this
+// occurs, the associated consumer's |fit::consumer::was_abandoned()| method
+// will return true and the consumer will not obtain any result from the task.
+// See |fit::consumer::promise()| and |fit::consumer::promise_or()| for
+// details on how abandonment of the task can be handled by the consumer.
+//
+// The consumption capability has a single owner represented by |fit::consumer|.
+// Its owner may exercise the capability to consume the task's result (as a
+/// promise), it may transfer the capability by moving it to another consumer
+// instance, or it may cause the asynchronous task to be "canceled" by
+// discarding the capability, implying that the task's result can never be
+// consumed. When this occurs, the associated completer's
+// |fit::completer::was_canceled()| method will return true and the task's
+// eventual result (if any) will be silently discarded.
+//
+// DECOUPLING
+//
+// See |fit::schedule_for_consumer()| for a helper which uses a bridge to
+// decouple completion and consumption of a task's result so they can be
+// performed on different executors.
+//
+// SYNOPSIS
+//
+// |V| is the type of value produced when the task completes successfully.
+// Use |std::tuple<Args...>| if the task produces multiple values, such as
+// when you intend to bind the task's completer to a callback with multiple
+// arguments using |fit::completer::bind_tuple()|.
+// Defaults to |void|.
+//
+// |E| is the type of error produced when the task completes with an error.
+// Defaults to |void|.
+//
+// EXAMPLE
+//
+// Imagine a File I/O library offers a callback-based asynchronous reading
+// function. We suppose that the read handling code will invoke the
+// callback upon completion. The library's API might look a bit like this:
+//
+// using read_callback = fit::function<void(size_t bytes_read)>;
+// void read_async(size_t num_bytes, uint8_t* buffer, read_callback cb);
+//
+// Here's how we can adapt the library's "read_async" function to a
+// |fit::promise| by binding its callback to a bridge:
+//
+// fit::promise<size_t> promise_read(uint8_t* buffer, size_t num_bytes) {
+// fit::bridge<size_t> bridge;
+// read_async(num_bytes, buffer, bridge.completer().bind());
+// return bridge.consumer().promise_or(::fit::error());
+// }
+//
+// Finally we can chain additional asynchronous tasks to be performed upon
+// completion of the promised read:
+//
+// uint8_t buffer[4096];
+// void my_program(fit::executor* executor) {
+// auto promise = promise_read(buffer, sizeof(buffer))
+// .and_then([] (size_t bytes_read) {
+// // consume contents of buffer
+// })
+// .or_else() {
+// // handle error case
+// });
+// executor->schedule_task(std::move(promise));
+// }
+//
+// Similarly, suppose the File I/O library offers a callback-based asynchronous
+// writing function that can return a variety of errors encoded as negative
+// sizes. Here's how we might decode those errors uniformly into |fit::result|
+// allowing them to be handled using combinators such as |or_else|.
+//
+// using write_callback = fit::function<void(size_t bytes_written, int error)>;
+// void write_async(size_t num_bytes, uint8_t* buffer, write_callback cb);
+//
+// fit::promise<size_t, int> promise_write(uint8_t* buffer, size_t num_bytes) {
+// fit::bridge<size_t, int> bridge;
+// write_async(num_bytes, buffer,
+// [completer = std::move(bridge.completer())](size_t bytes_written, int error) {
+// if (bytes_written == 0) {
+// completer.complete_error(error);
+// return;
+// }
+// completer.complete_ok(bytes_written);
+// });
+// return bridge.consumer().promise_or(::fit::error(ERR_ABANDONED));
+// }
+//
+// uint8_t buffer[4096];
+// void my_program(fit::executor* executor) {
+// auto promise = promise_write(buffer, sizeof(buffer))
+// .and_then([] (size_t bytes_written) {
+// // consume contents of buffer
+// })
+// .or_else(int error) {
+// // handle error case
+// });
+// executor->schedule_task(std::move(promise));
+// }
+//
+// See documentation of |fit::promise| for more information.
+template <typename V, typename E>
+class bridge final {
+ using bridge_state = ::fit::internal::bridge_state<V, E>;
+
+public:
+ using value_type = V;
+ using error_type = E;
+ using result_type = result<value_type, error_type>;
+ using completer_type = ::fit::completer<V, E>;
+ using consumer_type = ::fit::consumer<V, E>;
+
+ // Creates a bridge representing a new asynchronous task formed by the
+ // association of a completer and consumer.
+ bridge() {
+ bridge_state::create(&completer_.completion_ref_,
+ &consumer_.consumption_ref_);
+ }
+ bridge(bridge&& other) = default;
+ ~bridge() = default;
+
+ bridge& operator=(bridge&& other) = default;
+
+ // Gets a reference to the bridge's completer capability.
+ // The completer can be moved out of the bridge, if desired.
+ completer_type& completer() { return completer_; }
+ const completer_type& completer() const { return completer_; }
+
+ // Gets a reference to the bridge's consumer capability.
+ // The consumer can be moved out of the bridge, if desired.
+ consumer_type& consumer() { return consumer_; }
+ const consumer_type& consumer() const { return consumer_; }
+
+ bridge(const bridge& other) = delete;
+ bridge& operator=(const bridge& other) = delete;
+
+private:
+ completer_type completer_;
+ consumer_type consumer_;
+};
+
+// Provides a result upon completion of an asynchronous task.
+//
+// Instances of this class have single-ownership of a unique capability for
+// completing the task. This capability can be exercised at most once.
+// Ownership of the capability is implicitly transferred away when the
+// completer is abandoned, completed, or bound to a callback.
+//
+// See also |fit::bridge|.
+// See documentation of |fit::promise| for more information.
+//
+// SYNOPSIS
+//
+// |V| is the type of value produced when the task completes successfully.
+// Use |std::tuple<Args...>| if the task produces multiple values, such as
+// when you intend to bind the task's completer to a callback with multiple
+// arguments using |fit::completer::bind_tuple()|.
+// Defaults to |void|.
+//
+// |E| is the type of error produced when the task completes with an error.
+// Defaults to |void|.
+template <typename V, typename E>
+class completer final {
+ using bridge_state = ::fit::internal::bridge_state<V, E>;
+ using completion_ref = typename bridge_state::completion_ref;
+
+public:
+ using value_type = V;
+ using error_type = E;
+ using result_type = ::fit::result<V, E>;
+
+ completer() = default;
+ completer(completer&& other) = default;
+ ~completer() = default;
+
+ completer& operator=(completer&& other) = default;
+
+ // Returns true if this instance currently owns the unique capability for
+ // reporting completion of the task.
+ explicit operator bool() const { return !!completion_ref_; }
+
+ // Returns true if the associated |consumer| has canceled the task.
+ // This method returns a snapshot of the current cancelation state.
+ // Note that the task may be canceled concurrently at any time.
+ bool was_canceled() const {
+ assert(completion_ref_);
+ return completion_ref_.get()->was_canceled();
+ }
+
+ // Explicitly abandons the task, meaning that it will never be completed.
+ // See |fit::bridge| for details about abandonment.
+ void abandon() {
+ assert(completion_ref_);
+ completion_ref_ = completion_ref();
+ }
+
+ // Reports that the task has completed successfully.
+ // This method takes no arguments if |value_type| is void, otherwise it
+ // takes one argument which must be assignable to |value_type|.
+ template <typename VV = value_type,
+ typename = std::enable_if_t<std::is_void<VV>::value>>
+ void complete_ok() {
+ assert(completion_ref_);
+ bridge_state* state = completion_ref_.get();
+ state->complete_or_abandon(std::move(completion_ref_),
+ ::fit::ok());
+ }
+ template <typename VV = value_type,
+ typename = std::enable_if_t<!std::is_void<VV>::value>>
+ void complete_ok(VV value) {
+ assert(completion_ref_);
+ bridge_state* state = completion_ref_.get();
+ state->complete_or_abandon(std::move(completion_ref_),
+ ::fit::ok<value_type>(std::forward<VV>(value)));
+ }
+
+ // Reports that the task has completed with an error.
+ // This method takes no arguments if |error_type| is void, otherwise it
+ // takes one argument which must be assignable to |error_type|.
+ template <typename EE = error_type,
+ typename = std::enable_if_t<std::is_void<EE>::value>>
+ void complete_error() {
+ assert(completion_ref_);
+ bridge_state* state = completion_ref_.get();
+ state->complete_or_abandon(std::move(completion_ref_),
+ ::fit::error());
+ }
+ template <typename EE = error_type,
+ typename = std::enable_if_t<!std::is_void<EE>::value>>
+ void complete_error(EE error) {
+ assert(completion_ref_);
+ bridge_state* state = completion_ref_.get();
+ state->complete_or_abandon(std::move(completion_ref_),
+ ::fit::error<error_type>(std::forward<EE>(error)));
+ }
+
+ // Reports that the task has completed or been abandoned.
+ // See |fit::bridge| for details about abandonment.
+ //
+ // The result state determines the task's final disposition.
+ // - |fit::result_state::ok|: The task completed successfully.
+ // - |fit::result_state::error|: The task completed with an error.
+ // - |fit::result_state::pending|: The task was abandoned.
+ void complete_or_abandon(result_type result) {
+ assert(completion_ref_);
+ bridge_state* state = completion_ref_.get();
+ state->complete_or_abandon(std::move(completion_ref_),
+ std::move(result));
+ }
+
+ // Returns a callback that reports completion of the asynchronous task along
+ // with its result when invoked. This method is typically used to bind
+ // completion of a task to a callback that has zero or one argument.
+ //
+ // If |value_type| is void, the returned callback's signature is: void(void)
+ // Otherwise, the returned callback's signature is: void(value_type).
+ //
+ // The returned callback is thread-safe and move-only.
+ ::fit::internal::bridge_bind_callback<V, E> bind() {
+ assert(completion_ref_);
+ return ::fit::internal::bridge_bind_callback<V, E>(
+ std::move(completion_ref_));
+ }
+
+ // A variant of |bind()| that can be used to bind a completion of a task
+ // to a callback that has zero or more arguments by wrapping the callback's
+ // arguments into a tuple when producing the task's result.
+ //
+ // The |value_type| must be a tuple type.
+ // Given a |value_type| of std::tuple<Args...>, the returned callback's
+ // signature is: void(Args...). Note that the tuple's fields are
+ // unpacked as individual arguments of the callback.
+ //
+ // The returned callback is thread-safe and move-only.
+ ::fit::internal::bridge_bind_tuple_callback<V, E> bind_tuple() {
+ assert(completion_ref_);
+ return ::fit::internal::bridge_bind_tuple_callback<V, E>(
+ std::move(completion_ref_));
+ }
+
+ completer(const completer& other) = delete;
+ completer& operator=(const completer& other) = delete;
+
+private:
+ friend class bridge<V, E>;
+
+ completion_ref completion_ref_;
+};
+
+// Consumes the result of an asynchronous task.
+//
+// Instances of this class have single-ownership of a unique capability for
+// consuming the task's result. This capability can be exercised at most once.
+// Ownership of the capability is implicitly transferred away when the
+// task is canceled or converted to a promise.
+//
+// See also |fit::bridge|.
+// See documentation of |fit::promise| for more information.
+//
+// SYNOPSIS
+//
+// |V| is the type of value produced when the task completes successfully.
+// Use |std::tuple<Args...>| if the task produces multiple values, such as
+// when you intend to bind the task's completer to a callback with multiple
+// arguments using |fit::completer::bind_tuple()|.
+// Defaults to |void|.
+//
+// |E| is the type of error produced when the task completes with an error.
+// Defaults to |void|.
+template <typename V, typename E>
+class consumer final {
+ using bridge_state = ::fit::internal::bridge_state<V, E>;
+ using consumption_ref = typename bridge_state::consumption_ref;
+
+public:
+ using value_type = V;
+ using error_type = E;
+ using result_type = ::fit::result<V, E>;
+
+ consumer() = default;
+ consumer(consumer&& other) = default;
+ ~consumer() = default;
+
+ consumer& operator=(consumer&& other) = default;
+
+ // Returns true if this instance currently owns the unique capability for
+ // consuming the result of the task upon its completion.
+ explicit operator bool() const { return !!consumption_ref_; }
+
+ // Explicitly cancels the task, meaning that its result will never be consumed.
+ // See |fit::bridge| for details about cancelation.
+ void cancel() {
+ assert(consumption_ref_);
+ consumption_ref_ = consumption_ref();
+ }
+
+ // Returns true if the associated |completer| has abandoned the task.
+ // This method returns a snapshot of the current abandonment state.
+ // Note that the task may be abandoned concurrently at any time.
+ bool was_abandoned() const {
+ assert(consumption_ref_);
+ return consumption_ref_.get()->was_abandoned();
+ }
+
+ // Returns an unboxed promise which resumes execution once this task has
+ // completed. If the task is abandoned by its completer, the promise
+ // will not produce a result, thereby causing subsequent tasks associated
+ // with the promise to also be abandoned and eventually destroyed if
+ // they cannot make progress without the promised result.
+ promise_impl<typename bridge_state::promise_continuation>
+ promise() {
+ assert(consumption_ref_);
+ return make_promise_with_continuation(
+ typename bridge_state::promise_continuation(
+ std::move(consumption_ref_)));
+ }
+
+ // A variant of |promise()| that allows a default result to be provided when
+ // the task is abandoned by its completer. Typically this is used to cause
+ // the promise to return an error when the task is abandoned instead of
+ // causing subsequent tasks associated with the promise to also be abandoned.
+ //
+ // The state of |result_if_abandoned| determines the promise's behavior
+ // in case of abandonment.
+ //
+ // - |fit::result_state::ok|: Reports a successful result.
+ // - |fit::result_state::error|: Reports a failure result.
+ // - |fit::result_state::pending|: Does not report a result, thereby
+ // causing subsequent tasks associated with the promise to also be
+ // abandoned and eventually destroyed if they cannot make progress
+ // without the promised result.
+ promise_impl<typename bridge_state::promise_continuation>
+ promise_or(result_type result_if_abandoned) {
+ assert(consumption_ref_);
+ return make_promise_with_continuation(
+ typename bridge_state::promise_continuation(
+ std::move(consumption_ref_), std::move(result_if_abandoned)));
+ }
+
+ consumer(const consumer& other) = delete;
+ consumer& operator=(const consumer& other) = delete;
+
+private:
+ friend class bridge<V, E>;
+
+ consumption_ref consumption_ref_;
+};
+
+// Schedules |promise| to run on |executor| and returns a |consumer| which
+// receives the result of the promise upon its completion.
+//
+// This method has the effect of decoupling the evaluation of a promise from
+// the consumption of its result such that they can be performed on different
+// executors (possibly on different threads).
+//
+// |executor| must be non-null.
+// |promise| must be non-empty.
+//
+// EXAMPLE
+//
+// This example shows an object that encapsulates its own executor which it
+// manages independently from that of its clients. This enables the object
+// to obtain certain assurances such as a guarantee of single-threaded
+// execution for its internal operations even if its clients happen to be
+// multi-threaded (or vice-versa as desired).
+//
+// // This model has specialized internal threading requirements so it
+// // manages its own executor.
+// class model {
+// public:
+// fit::consumer<int> perform_calculation(int parameter) {
+// return fit::schedule_for_consumer(&executor_,
+// fit::make_promise([parameter] {
+// // In reality, this would likely be a much more
+// // complex expression.
+// return fit::ok(parameter * parameter);
+// });
+// }
+//
+// private:
+// // The model is responsible for initializing and running its own
+// // executor (perhaps on its own thread).
+// fit::single_threaded_executor executor_;
+// };
+//
+// // Asks the model to perform a calculation, awaits a result on the
+// // provided executor (which is different from the one internally used
+// // by the model), then prints the result.
+// void print_output(fit::executor* executor, model* m) {
+// executor->schedule_task(
+// m->perform_calculation(16)
+// .promise_or(fit::error())
+// .and_then([] (int result) { printf("done: %d\n", result); })
+// .or_else([] { puts("failed or abandoned"); }));
+// }
+//
+template <typename Promise>
+inline consumer<typename Promise::value_type, typename Promise::error_type>
+schedule_for_consumer(fit::executor* executor, Promise promise) {
+ assert(executor);
+ assert(promise);
+ fit::bridge<typename Promise::value_type,
+ typename Promise::error_type>
+ bridge;
+ executor->schedule_task(
+ promise.then([completer = std::move(bridge.completer())](
+ typename Promise::result_type& result) mutable {
+ completer.complete_or_abandon(std::move(result));
+ }));
+ return std::move(bridge.consumer());
+}
+
+} // namespace fit
+
+#endif // LIB_FIT_BRIDGE_H_
diff --git a/pkg/fit/include/lib/fit/bridge_internal.h b/pkg/fit/include/lib/fit/bridge_internal.h
new file mode 100644
index 0000000..8f805ee
--- /dev/null
+++ b/pkg/fit/include/lib/fit/bridge_internal.h
@@ -0,0 +1,401 @@
+// 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.
+
+#ifndef LIB_FIT_BRIDGE_INTERNAL_H_
+#define LIB_FIT_BRIDGE_INTERNAL_H_
+
+#include <atomic>
+#include <mutex>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "promise.h"
+#include "result.h"
+#include "thread_safety.h"
+
+namespace fit {
+namespace internal {
+
+// State shared between one completer and one consumer.
+// This object is somewhat unusual in that it has dual-ownership represented
+// by a pair of single-ownership references: a |completion_ref| and a
+// |consumption_ref|.
+//
+// The bridge's state evolves as follows:
+// - Initially the bridge's disposition is "pending".
+// - When the completer produces a result, the bridge's disposition
+// becomes "completed".
+// - When the completer drops its ref without producing a result,
+// the bridge's disposition becomes "abandoned".
+// - When the consumer drops its ref without consuming the result,
+// the bridge's disposition becomes "canceled".
+// - When a full rendezvous between completer and consumer takes place,
+// the bridge's disposition becomes "returned".
+// - When both refs are dropped, the bridge state is destroyed.
+template <typename V, typename E>
+class bridge_state final {
+public:
+ class completion_ref;
+ class consumption_ref;
+ class promise_continuation;
+
+ using result_type = result<V, E>;
+
+ ~bridge_state() = default;
+
+ static void create(completion_ref* out_completion_ref,
+ consumption_ref* out_consumption_ref);
+
+ bool was_canceled() const;
+ bool was_abandoned() const;
+ void complete_or_abandon(completion_ref ref, result_type result);
+
+ bridge_state(const bridge_state&) = delete;
+ bridge_state(bridge_state&&) = delete;
+ bridge_state& operator=(const bridge_state&) = delete;
+ bridge_state& operator=(bridge_state&&) = delete;
+
+private:
+ enum class disposition {
+ pending,
+ abandoned,
+ completed,
+ canceled,
+ returned
+ };
+
+ bridge_state() = default;
+
+ void drop_completion_ref(bool was_completed);
+ void drop_consumption_ref(bool was_consumed);
+ void drop_ref_and_maybe_delete_self();
+ void set_result_if_abandoned(result_type result_if_abandoned);
+ result_type await_result(consumption_ref* ref, ::fit::context& context);
+ void deliver_result() FIT_REQUIRES(mutex_);
+
+ mutable std::mutex mutex_;
+
+ // Ref-count for completion and consumption.
+ // There can only be one of each ref type so the initial count is 2.
+ std::atomic<uint32_t> ref_count_{2};
+
+ // The disposition of the bridge.
+ disposition disposition_ FIT_GUARDED(mutex_) = {disposition::pending};
+
+ // The suspended task.
+ // Invariant: Only valid when disposition is |pending|.
+ suspended_task task_ FIT_GUARDED(mutex_);
+
+ // The result in flight.
+ // Invariant: Only valid when disposition is |pending|, |completed|,
+ // or |abandoned|.
+ result_type result_ FIT_GUARDED(mutex_);
+};
+
+// The unique capability held by a bridge's completer.
+template <typename V, typename E>
+class bridge_state<V, E>::completion_ref final {
+public:
+ completion_ref()
+ : state_(nullptr) {}
+
+ explicit completion_ref(bridge_state* state)
+ : state_(state) {} // adopts existing reference
+
+ completion_ref(completion_ref&& other)
+ : state_(other.state_) {
+ other.state_ = nullptr;
+ }
+
+ ~completion_ref() {
+ if (state_)
+ state_->drop_completion_ref(false /*was_completed*/);
+ }
+
+ completion_ref& operator=(completion_ref&& other) {
+ if (&other == this)
+ return *this;
+ if (state_)
+ state_->drop_completion_ref(false /*was_completed*/);
+ state_ = other.state_;
+ other.state_ = nullptr;
+ return *this;
+ }
+
+ explicit operator bool() const { return !!state_; }
+
+ bridge_state* get() const { return state_; }
+
+ void drop_after_completion() {
+ state_->drop_completion_ref(true /*was_completed*/);
+ state_ = nullptr;
+ }
+
+ completion_ref(const completion_ref& other) = delete;
+ completion_ref& operator=(const completion_ref& other) = delete;
+
+private:
+ bridge_state* state_;
+};
+
+// The unique capability held by a bridge's consumer.
+template <typename V, typename E>
+class bridge_state<V, E>::consumption_ref final {
+public:
+ consumption_ref()
+ : state_(nullptr) {}
+
+ explicit consumption_ref(bridge_state* state)
+ : state_(state) {} // adopts existing reference
+
+ consumption_ref(consumption_ref&& other)
+ : state_(other.state_) {
+ other.state_ = nullptr;
+ }
+
+ ~consumption_ref() {
+ if (state_)
+ state_->drop_consumption_ref(false /*was_consumed*/);
+ }
+
+ consumption_ref& operator=(consumption_ref&& other) {
+ if (&other == this)
+ return *this;
+ if (state_)
+ state_->drop_consumption_ref(false /*was_consumed*/);
+ state_ = other.state_;
+ other.state_ = nullptr;
+ return *this;
+ }
+
+ explicit operator bool() const { return !!state_; }
+
+ bridge_state* get() const { return state_; }
+
+ void drop_after_consumption() {
+ state_->drop_consumption_ref(true /*was_consumed*/);
+ state_ = nullptr;
+ }
+
+ consumption_ref(const consumption_ref& other) = delete;
+ consumption_ref& operator=(const consumption_ref& other) = delete;
+
+private:
+ bridge_state* state_;
+};
+
+// The continuation produced by |consumer::promise()| and company.
+template <typename V, typename E>
+class bridge_state<V, E>::promise_continuation final {
+public:
+ explicit promise_continuation(consumption_ref ref)
+ : ref_(std::move(ref)) {}
+
+ promise_continuation(consumption_ref ref,
+ result_type result_if_abandoned)
+ : ref_(std::move(ref)) {
+ ref_.get()->set_result_if_abandoned(std::move(result_if_abandoned));
+ }
+
+ result_type operator()(::fit::context& context) {
+ return ref_.get()->await_result(&ref_, context);
+ }
+
+private:
+ consumption_ref ref_;
+};
+
+// The callback produced by |completer::bind()|.
+template <typename V, typename E>
+class bridge_bind_callback final {
+ using bridge_state = bridge_state<V, E>;
+
+public:
+ explicit bridge_bind_callback(typename bridge_state::completion_ref ref)
+ : ref_(std::move(ref)) {}
+
+ template <typename VV = V,
+ typename = std::enable_if_t<std::is_void<VV>::value>>
+ void operator()() {
+ bridge_state* state = ref_.get();
+ state->complete_or_abandon(std::move(ref_), ::fit::ok());
+ }
+
+ template <typename VV = V,
+ typename = std::enable_if_t<!std::is_void<VV>::value>>
+ void operator()(VV value) {
+ bridge_state* state = ref_.get();
+ state->complete_or_abandon(std::move(ref_),
+ ::fit::ok<V>(std::forward<VV>(value)));
+ }
+
+private:
+ typename bridge_state::completion_ref ref_;
+};
+
+// The callback produced by |completer::bind_tuple()|.
+template <typename V, typename E>
+class bridge_bind_tuple_callback;
+template <typename... Args, typename E>
+class bridge_bind_tuple_callback<std::tuple<Args...>, E> final {
+ using bridge_state = bridge_state<std::tuple<Args...>, E>;
+
+public:
+ explicit bridge_bind_tuple_callback(typename bridge_state::completion_ref ref)
+ : ref_(std::move(ref)) {}
+
+ void operator()(Args... args) {
+ bridge_state* state = ref_.get();
+ state->complete_or_abandon(
+ std::move(ref_),
+ ::fit::ok(std::make_tuple<Args...>(std::forward<Args>(args)...)));
+ }
+
+private:
+ typename bridge_state::completion_ref ref_;
+};
+
+template <typename V, typename E>
+void bridge_state<V, E>::create(completion_ref* out_completion_ref,
+ consumption_ref* out_consumption_ref) {
+ bridge_state* state = new bridge_state();
+ *out_completion_ref = completion_ref(state);
+ *out_consumption_ref = consumption_ref(state);
+}
+
+template <typename V, typename E>
+bool bridge_state<V, E>::was_canceled() const {
+ std::lock_guard<std::mutex> lock(mutex_);
+ return disposition_ == disposition::canceled;
+}
+
+template <typename V, typename E>
+bool bridge_state<V, E>::was_abandoned() const {
+ std::lock_guard<std::mutex> lock(mutex_);
+ return disposition_ == disposition::abandoned;
+}
+
+template <typename V, typename E>
+void bridge_state<V, E>::drop_completion_ref(bool was_completed) {
+ if (!was_completed) {
+ // The task was abandoned.
+ std::lock_guard<std::mutex> lock(mutex_);
+ assert(disposition_ == disposition::pending ||
+ disposition_ == disposition::canceled);
+ if (disposition_ == disposition::pending) {
+ disposition_ = disposition::abandoned;
+ deliver_result();
+ }
+ }
+ drop_ref_and_maybe_delete_self();
+}
+
+template <typename V, typename E>
+void bridge_state<V, E>::drop_consumption_ref(bool was_consumed) {
+ if (!was_consumed) {
+ // The task was canceled.
+ std::lock_guard<std::mutex> lock(mutex_);
+ assert(disposition_ == disposition::pending ||
+ disposition_ == disposition::completed ||
+ disposition_ == disposition::abandoned);
+ if (disposition_ == disposition::pending) {
+ disposition_ = disposition::canceled;
+ result_ = ::fit::pending();
+ task_.reset(); // there is no task to wake up anymore
+ }
+ }
+ drop_ref_and_maybe_delete_self();
+}
+
+template <typename V, typename E>
+void bridge_state<V, E>::drop_ref_and_maybe_delete_self() {
+ uint32_t count = ref_count_.fetch_sub(1u, std::memory_order_release) - 1u;
+ assert(count >= 0);
+ if (count == 0) {
+ std::atomic_thread_fence(std::memory_order_acquire);
+ delete this;
+ }
+}
+
+template <typename V, typename E>
+void bridge_state<V, E>::complete_or_abandon(completion_ref ref,
+ result_type result) {
+ assert(ref.get() == this);
+ if (result.is_pending())
+ return; // let the ref go out of scope to abandon the task
+
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ assert(disposition_ == disposition::pending ||
+ disposition_ == disposition::canceled);
+ if (disposition_ == disposition::pending) {
+ disposition_ = disposition::completed;
+ result_ = std::move(result);
+ deliver_result();
+ }
+ }
+ // drop the reference ouside of the lock
+ ref.drop_after_completion();
+}
+
+template <typename V, typename E>
+void bridge_state<V, E>::set_result_if_abandoned(
+ result_type result_if_abandoned) {
+ if (result_if_abandoned.is_pending())
+ return; // nothing to do
+
+ std::lock_guard<std::mutex> lock(mutex_);
+ assert(disposition_ == disposition::pending ||
+ disposition_ == disposition::completed ||
+ disposition_ == disposition::abandoned);
+ if (disposition_ == disposition::pending ||
+ disposition_ == disposition::abandoned) {
+ result_ = std::move(result_if_abandoned);
+ }
+}
+
+template <typename V, typename E>
+typename bridge_state<V, E>::result_type bridge_state<V, E>::await_result(
+ consumption_ref* ref, ::fit::context& context) {
+ assert(ref->get() == this);
+ result_type result;
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ assert(disposition_ == disposition::pending ||
+ disposition_ == disposition::completed ||
+ disposition_ == disposition::abandoned);
+ if (disposition_ == disposition::pending) {
+ task_ = context.suspend_task();
+ return ::fit::pending();
+ }
+ disposition_ = disposition::returned;
+ result = std::move(result_);
+ }
+ // drop the reference ouside of the lock
+ ref->drop_after_consumption();
+ return result;
+}
+
+template <typename V, typename E>
+void bridge_state<V, E>::deliver_result() {
+ if (result_.is_pending()) {
+ task_.reset(); // the task has been canceled
+ } else {
+ task_.resume_task(); // we have a result so wake up the task
+ }
+}
+
+} // namespace internal
+
+template <typename V = void, typename E = void>
+class bridge;
+template <typename V = void, typename E = void>
+class completer;
+template <typename V = void, typename E = void>
+class consumer;
+
+} // namespace fit
+
+#endif // LIB_FIT_BRIDGE_INTERNAL_H_
diff --git a/pkg/fit/include/lib/fit/defer.h b/pkg/fit/include/lib/fit/defer.h
new file mode 100644
index 0000000..7d85048
--- /dev/null
+++ b/pkg/fit/include/lib/fit/defer.h
@@ -0,0 +1,138 @@
+// 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.
+
+#ifndef LIB_FIT_DEFER_H_
+#define LIB_FIT_DEFER_H_
+
+#include <utility>
+
+#include "nullable.h"
+
+namespace fit {
+
+// A move-only deferred action wrapper with RAII semantics.
+// This class is not thread safe.
+//
+// The wrapper holds a function-like callable target with no arguments
+// which it invokes when it goes out of scope unless canceled, called, or
+// moved to a wrapper in a different scope.
+//
+// See |fit::defer()| for idiomatic usage.
+template <typename T>
+class deferred_action final {
+public:
+ // Creates a deferred action without a pending target.
+ deferred_action() = default;
+ explicit deferred_action(decltype(nullptr)) {}
+
+ // Creates a deferred action with a pending target.
+ explicit deferred_action(T target)
+ : target_(std::move(target)) {}
+
+ // Creates a deferred action with a pending target moved from another
+ // deferred action, leaving the other one without a pending target.
+ deferred_action(deferred_action&& other) = default;
+
+ // Invokes and releases the deferred action's pending target (if any).
+ ~deferred_action() {
+ call();
+ }
+
+ // Returns true if the deferred action has a pending target.
+ explicit operator bool() const {
+ return !!target_;
+ }
+
+ // Invokes and releases the deferred action's pending target (if any),
+ // then move-assigns it from another deferred action, leaving the latter
+ // one without a pending target.
+ deferred_action& operator=(deferred_action&& other) {
+ if (&other == this)
+ return *this;
+ call();
+ target_ = std::move(other.target_);
+ return *this;
+ }
+
+ // Invokes and releases the deferred action's pending target (if any).
+ void call() {
+ if (target_) {
+ // Move to a local to guard against re-entrance.
+ T local_target = std::move(*target_);
+ target_.reset();
+ local_target();
+ }
+ }
+
+ // Releases the deferred action's pending target (if any) without
+ // invoking it.
+ void cancel() {
+ target_.reset();
+ }
+ deferred_action& operator=(decltype(nullptr)) {
+ cancel();
+ return *this;
+ }
+
+ // Assigns a new target to the deferred action.
+ deferred_action& operator=(T target) {
+ target_ = std::move(target);
+ return *this;
+ }
+
+ deferred_action(const deferred_action& other) = delete;
+ deferred_action& operator=(const deferred_action& other) = delete;
+
+private:
+ nullable<T> target_;
+};
+
+template <typename T>
+bool operator==(const deferred_action<T>& action, decltype(nullptr)) {
+ return !action;
+}
+template <typename T>
+bool operator==(decltype(nullptr), const deferred_action<T>& action) {
+ return !action;
+}
+template <typename T>
+bool operator!=(const deferred_action<T>& action, decltype(nullptr)) {
+ return !!action;
+}
+template <typename T>
+bool operator!=(decltype(nullptr), const deferred_action<T>& action) {
+ return !!action;
+}
+
+// Defers execution of a function-like callable target with no arguments
+// until the value returned by this function goes out of scope unless canceled,
+// called, or moved to a wrapper in a different scope.
+//
+// // This example prints "Hello..." then "Goodbye!".
+// void test() {
+// auto d = fit::defer([]{ puts("Goodbye!"); });
+// puts("Hello...");
+// }
+//
+// // This example prints nothing because the deferred action is canceled.
+// void do_nothing() {
+// auto d = fit::defer([]{ puts("I'm not here."); });
+// d.cancel();
+// }
+//
+// // This example shows how the deferred action can be reassigned assuming
+// // the new target has the same type and the old one, in this case by
+// // representing the target as a |fit::closure|.
+// void reassign() {
+// auto d = fit::defer<fit::closure>([] { puts("This runs first."); });
+// d = fit::defer<fit::closure>([] { puts("This runs afterwards."); });
+// }
+template <typename T>
+inline deferred_action<T> defer(T target) {
+ return deferred_action<T>(std::move(target));
+}
+
+} // namespace fit
+
+#endif // LIB_FIT_DEFER_H_
diff --git a/pkg/fit/include/lib/fit/function.h b/pkg/fit/include/lib/fit/function.h
new file mode 100644
index 0000000..5aaecca
--- /dev/null
+++ b/pkg/fit/include/lib/fit/function.h
@@ -0,0 +1,315 @@
+// Copyright 2017 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.
+
+#ifndef LIB_FIT_FUNCTION_H_
+#define LIB_FIT_FUNCTION_H_
+
+#include <memory>
+#include <type_traits>
+
+#include "function_internal.h"
+#include "nullable.h"
+
+namespace fit {
+
+template <size_t inline_target_size, bool require_inline,
+ typename Result, typename... Args>
+class function_impl;
+
+// The default size allowance for storing a target inline within a function
+// object, in bytes. This default allows for inline storage of targets
+// as big as two pointers, such as an object pointer and a pointer to a member
+// function.
+constexpr size_t default_inline_target_size = sizeof(void*) * 2;
+
+// A |fit::function| is a move-only polymorphic function wrapper.
+//
+// |fit::function<T>| behaves like |std::function<T>| except that it is move-only
+// instead of copyable so it can hold targets which cannot be copied, such as
+// mutable lambdas.
+//
+// Targets of up to |inline_target_size| bytes in size (rounded up for memory
+// alignment) are stored inline within the function object without incurring
+// any heap allocation. Larger callable objects will be moved to the heap as
+// required.
+//
+// See also |fit::inline_function<T, size>| for more control over allocation
+// behavior.
+//
+// SYNOPSIS
+//
+// |T| is the function's signature. e.g. void(int, std::string).
+//
+// |inline_target_size| is the minimum size of target that is guaranteed to
+// fit within a function without requiring heap allocation.
+// Defaults to |default_inline_target_size|.
+//
+// Class members are documented in |fit::function_impl|.
+//
+// EXAMPLES
+//
+// - https://fuchsia.googlesource.com/zircon/+/master/system/utest/fit/examples/function_example1.cpp
+// - https://fuchsia.googlesource.com/zircon/+/master/system/utest/fit/examples/function_example2.cpp
+//
+template <typename T, size_t inline_target_size = default_inline_target_size>
+using function = function_impl<inline_target_size, false, T>;
+
+// A move-only callable object wrapper which forces callables to be stored inline
+// and never performs heap allocation.
+//
+// Behaves just like |fit::function<T, inline_target_size>| except that attempting
+// to store a target larger than |inline_target_size| will fail to compile.
+template <typename T, size_t inline_target_size = default_inline_target_size>
+using inline_function = function_impl<inline_target_size, true, T>;
+
+// Synonym for a function which takes no arguments and produces no result.
+using closure = function<void()>;
+
+// Function implementation details.
+// See |fit::function| documentation for more information.
+template <size_t inline_target_size, bool require_inline,
+ typename Result, typename... Args>
+class function_impl<inline_target_size, require_inline, Result(Args...)> final {
+ using ops_type = const ::fit::internal::target_ops<Result, Args...>*;
+ using storage_type = typename std::aligned_storage<
+ (inline_target_size >= sizeof(void*)
+ ? inline_target_size
+ : sizeof(void*))>::type; // avoid including <algorithm> just for max
+ template <typename Callable>
+ using target_type = ::fit::internal::target<
+ Callable,
+ (sizeof(Callable) <= sizeof(storage_type)),
+ Result, Args...>;
+ using null_target_type = target_type<decltype(nullptr)>;
+
+public:
+ // The function's result type.
+ using result_type = Result;
+
+ // // Creates a function with an empty target.
+ function_impl() {
+ initialize_null_target();
+ }
+
+ // Creates a function with an empty target.
+ function_impl(decltype(nullptr)) {
+ initialize_null_target();
+ }
+
+ // Creates a function bound to the specified function pointer.
+ // If target == nullptr, assigns an empty target.
+ function_impl(Result (*target)(Args...)) {
+ initialize_target(target);
+ }
+
+ // Creates a function bound to the specified callable object.
+ // If target == nullptr, assigns an empty target.
+ //
+ // For functors, we need to capture the raw type but also restrict on the existence of an
+ // appropriate operator () to resolve overloads and implicit casts properly.
+ template <typename Callable,
+ typename = std::enable_if_t<
+ std::is_convertible<
+ decltype(std::declval<Callable&>()(
+ std::declval<Args>()...)),
+ result_type>::value>>
+ function_impl(Callable target) {
+ initialize_target(std::move(target));
+ }
+
+ // Creates a function with a target moved from another function,
+ // leaving the other function with an empty target.
+ function_impl(function_impl&& other) {
+ move_target_from(std::move(other));
+ }
+
+ // Destroys the function, releasing its target.
+ ~function_impl() {
+ destroy_target();
+ }
+
+ // Returns true if the function has a non-empty target.
+ explicit operator bool() const {
+ return ops_ != &null_target_type::ops;
+ };
+
+ // Invokes the function's target.
+ // Aborts if the function's target is empty.
+ Result operator()(Args... args) const {
+ return ops_->invoke(&bits_, std::forward<Args>(args)...);
+ }
+
+ // Assigns an empty target.
+ function_impl& operator=(decltype(nullptr)) {
+ destroy_target();
+ initialize_null_target();
+ return *this;
+ }
+
+ // Assigns the function's target.
+ // If target == nullptr, assigns an empty target.
+ template <typename Callable,
+ typename = std::enable_if_t<
+ std::is_convertible<
+ decltype(std::declval<Callable&>()(
+ std::declval<Args>()...)),
+ result_type>::value>>
+ function_impl& operator=(Callable target) {
+ destroy_target();
+ initialize_target(std::move(target));
+ return *this;
+ }
+
+ // Assigns the function with a target moved from another function,
+ // leaving the other function with an empty target.
+ function_impl& operator=(function_impl&& other) {
+ if (&other == this)
+ return *this;
+ destroy_target();
+ move_target_from(std::move(other));
+ return *this;
+ }
+
+ // Swaps the functions' targets.
+ void swap(function_impl& other) {
+ if (&other == this)
+ return;
+ ops_type temp_ops = ops_;
+ storage_type temp_bits;
+ ops_->move(&bits_, &temp_bits);
+
+ ops_ = other.ops_;
+ other.ops_->move(&other.bits_, &bits_);
+
+ other.ops_ = temp_ops;
+ temp_ops->move(&temp_bits, &other.bits_);
+ }
+
+ // Returns a pointer to the function's target.
+ template <typename Callable>
+ Callable* target() {
+ check_target_type<Callable>();
+ return static_cast<Callable*>(ops_->get(&bits_));
+ }
+
+ // Returns a pointer to the function's target.
+ template <typename Callable>
+ const Callable* target() const {
+ check_target_type<Callable>();
+ return static_cast<Callable*>(ops_->get(&bits_));
+ }
+
+ // Returns a new function object which invokes the same target.
+ // The target itself is not copied; it is moved to the heap and its
+ // lifetime is extended until all references have been released.
+ //
+ // Note: This method is not supported on |fit::inline_function<>|
+ // because it may incur a heap allocation which is contrary to
+ // the stated purpose of |fit::inline_function<>|.
+ function_impl share() {
+ static_assert(!require_inline, "Inline functions cannot be shared.");
+ // TODO(jeffbrown): Replace shared_ptr with a better ref-count mechanism.
+ // TODO(jeffbrown): This definition breaks the client's ability to use
+ // |target()| because the target's type has changed. We could fix this
+ // by defining a new target type (and vtable) for shared targets
+ // although it would be nice to avoid memory overhead and code expansion
+ // when sharing is not used.
+ struct ref {
+ std::shared_ptr<function_impl> target;
+ Result operator()(Args... args) {
+ return (*target)(std::forward<Args>(args)...);
+ }
+ };
+ if (ops_ != &target_type<ref>::ops) {
+ if (ops_ == &null_target_type::ops) {
+ return nullptr;
+ }
+ auto target = ref{std::make_shared<function_impl>(std::move(*this))};
+ *this = std::move(target);
+ }
+ return function_impl(*static_cast<ref*>(ops_->get(&bits_)));
+ }
+
+ function_impl(const function_impl& other) = delete;
+ function_impl& operator=(const function_impl& other) = delete;
+
+private:
+ // assumes target is uninitialized
+ void initialize_null_target() {
+ ops_ = &null_target_type::ops;
+ }
+
+ // assumes target is uninitialized
+ template <typename Callable>
+ void initialize_target(Callable target) {
+ static_assert(!require_inline || sizeof(Callable) <= inline_target_size,
+ "Callable too large to store inline as requested.");
+ if (is_null(target)) {
+ initialize_null_target();
+ } else {
+ ops_ = &target_type<Callable>::ops;
+ target_type<Callable>::initialize(&bits_, std::move(target));
+ }
+ }
+
+ // leaves target uninitialized
+ void destroy_target() {
+ ops_->destroy(&bits_);
+ }
+
+ // leaves other target initialized to null
+ void move_target_from(function_impl&& other) {
+ ops_ = other.ops_;
+ other.ops_->move(&other.bits_, &bits_);
+ other.initialize_null_target();
+ }
+
+ template <typename Callable>
+ void check_target_type() const {
+ if (ops_ != &target_type<Callable>::ops)
+ abort();
+ }
+
+ ops_type ops_;
+ mutable storage_type bits_;
+}; // namespace fit
+
+template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
+void swap(function_impl<inline_target_size, require_inline, Result, Args...>& a,
+ function_impl<inline_target_size, require_inline, Result, Args...>& b) {
+ a.swap(b);
+}
+
+template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
+bool operator==(const function_impl<inline_target_size, require_inline, Result, Args...>& f,
+ decltype(nullptr)) {
+ return !f;
+}
+template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
+bool operator==(decltype(nullptr),
+ const function_impl<inline_target_size, require_inline, Result, Args...>& f) {
+ return !f;
+}
+template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
+bool operator!=(const function_impl<inline_target_size, require_inline, Result, Args...>& f,
+ decltype(nullptr)) {
+ return !!f;
+}
+template <size_t inline_target_size, bool require_inline, typename Result, typename... Args>
+bool operator!=(decltype(nullptr),
+ const function_impl<inline_target_size, require_inline, Result, Args...>& f) {
+ return !!f;
+}
+
+// Returns a Callable object which invokes a member function of an object.
+template <typename R, typename T, typename... Args>
+auto bind_member(T* instance, R (T::*fn)(Args...)) {
+ return [instance, fn](Args... args) {
+ return (instance->*fn)(std::forward<Args>(args)...);
+ };
+}
+
+} // namespace fit
+
+#endif // LIB_FIT_FUNCTION_H_
diff --git a/pkg/fit/include/lib/fit/function_internal.h b/pkg/fit/include/lib/fit/function_internal.h
new file mode 100644
index 0000000..ddcefef
--- /dev/null
+++ b/pkg/fit/include/lib/fit/function_internal.h
@@ -0,0 +1,121 @@
+// Copyright 2017 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.
+
+#ifndef LIB_FIT_FUNCTION_INTERNAL_H_
+#define LIB_FIT_FUNCTION_INTERNAL_H_
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <new>
+#include <type_traits>
+#include <utility>
+
+namespace fit {
+namespace internal {
+
+template <typename Result, typename... Args>
+struct target_ops final {
+ void* (*get)(void* bits);
+ Result (*invoke)(void* bits, Args... args);
+ void (*move)(void* from_bits, void* to_bits);
+ void (*destroy)(void* bits);
+};
+
+template <typename Callable, bool is_inline, typename Result, typename... Args>
+struct target;
+
+template <typename Result, typename... Args>
+struct target<decltype(nullptr), true, Result, Args...> final {
+ static Result invoke(void* bits, Args... args) {
+ abort();
+ }
+
+ static const target_ops<Result, Args...> ops;
+};
+
+inline void* null_target_get(void* bits) {
+ return nullptr;
+}
+inline void null_target_move(void* from_bits, void* to_bits) {}
+inline void null_target_destroy(void* bits) {}
+
+template <typename Result, typename... Args>
+constexpr target_ops<Result, Args...> target<decltype(nullptr), true, Result, Args...>::ops = {
+ &null_target_get,
+ &target::invoke,
+ &null_target_move,
+ &null_target_destroy};
+
+template <typename Callable, typename Result, typename... Args>
+struct target<Callable, true, Result, Args...> final {
+ static void initialize(void* bits, Callable&& target) {
+ new (bits) Callable(std::move(target));
+ }
+ static Result invoke(void* bits, Args... args) {
+ auto& target = *static_cast<Callable*>(bits);
+ return target(std::forward<Args>(args)...);
+ }
+ static void move(void* from_bits, void* to_bits) {
+ auto& from_target = *static_cast<Callable*>(from_bits);
+ new (to_bits) Callable(std::move(from_target));
+ from_target.~Callable();
+ }
+ static void destroy(void* bits) {
+ auto& target = *static_cast<Callable*>(bits);
+ target.~Callable();
+ }
+
+ static const target_ops<Result, Args...> ops;
+};
+
+inline void* inline_target_get(void* bits) {
+ return bits;
+}
+
+template <typename Callable, typename Result, typename... Args>
+constexpr target_ops<Result, Args...> target<Callable, true, Result, Args...>::ops = {
+ &inline_target_get,
+ &target::invoke,
+ &target::move,
+ &target::destroy};
+
+template <typename Callable, typename Result, typename... Args>
+struct target<Callable, false, Result, Args...> final {
+ static void initialize(void* bits, Callable&& target) {
+ auto ptr = static_cast<Callable**>(bits);
+ *ptr = new Callable(std::move(target));
+ }
+ static Result invoke(void* bits, Args... args) {
+ auto& target = **static_cast<Callable**>(bits);
+ return target(std::forward<Args>(args)...);
+ }
+ static void move(void* from_bits, void* to_bits) {
+ auto from_ptr = static_cast<Callable**>(from_bits);
+ auto to_ptr = static_cast<Callable**>(to_bits);
+ *to_ptr = *from_ptr;
+ }
+ static void destroy(void* bits) {
+ auto ptr = static_cast<Callable**>(bits);
+ delete *ptr;
+ }
+
+ static const target_ops<Result, Args...> ops;
+};
+
+inline void* heap_target_get(void* bits) {
+ return *static_cast<void**>(bits);
+}
+
+template <typename Callable, typename Result, typename... Args>
+constexpr target_ops<Result, Args...> target<Callable, false, Result, Args...>::ops = {
+ &heap_target_get,
+ &target::invoke,
+ &target::move,
+ &target::destroy};
+
+} // namespace internal
+} // namespace fit
+
+#endif // LIB_FIT_FUNCTION_INTERNAL_H_
diff --git a/pkg/fit/include/lib/fit/function_traits.h b/pkg/fit/include/lib/fit/function_traits.h
new file mode 100644
index 0000000..3e0c2a3
--- /dev/null
+++ b/pkg/fit/include/lib/fit/function_traits.h
@@ -0,0 +1,18 @@
+// 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.
+
+#ifndef LIB_FIT_FUNCTION_TRAITS_H_
+#define LIB_FIT_FUNCTION_TRAITS_H_
+
+#include "traits.h"
+
+namespace fit {
+
+// function_traits is deprecated, please use callable_traits
+template <typename T>
+using function_traits = callable_traits<T>;
+
+} // namespace fit
+
+#endif // LIB_FIT_FUNCTION_TRAITS_H_
diff --git a/pkg/fit/include/lib/fit/nullable.h b/pkg/fit/include/lib/fit/nullable.h
new file mode 100644
index 0000000..a6050af
--- /dev/null
+++ b/pkg/fit/include/lib/fit/nullable.h
@@ -0,0 +1,245 @@
+// 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.
+
+#ifndef LIB_FIT_NULLABLE_H_
+#define LIB_FIT_NULLABLE_H_
+
+#include <assert.h>
+
+#include <type_traits>
+#include <utility>
+
+#include "optional.h"
+
+namespace fit {
+
+// Determines whether a type can be compared with nullptr.
+template <typename T, typename Comparable = bool>
+struct is_comparable_with_null : public std::false_type {};
+template <typename T>
+struct is_comparable_with_null<T, decltype(std::declval<const T&>() == nullptr)>
+ : public std::true_type {};
+
+// Returns true if a value equals nullptr.
+template <typename T, typename Comparable = bool>
+struct is_null_predicate {
+ constexpr bool operator()(const T& value) { return false; }
+};
+template <typename T>
+struct is_null_predicate<T, decltype(std::declval<const T&>() == nullptr)> {
+ constexpr bool operator()(const T& value) { return value == nullptr; }
+};
+template <typename T>
+constexpr inline bool is_null(const T& value) {
+ return is_null_predicate<T>()(value);
+}
+
+// Determines whether a type can be initialized, assigned, and compared
+// with nullptr.
+template <typename T>
+struct is_nullable
+ : public std::integral_constant<
+ bool,
+ std::is_constructible<T, decltype(nullptr)>::value &&
+ std::is_assignable<T&, decltype(nullptr)>::value &&
+ is_comparable_with_null<T>::value> {};
+template <>
+struct is_nullable<void> : public std::false_type {};
+
+// Holds a value or nullptr.
+//
+// This class is similar to |std::optional<T>| except that it uses less
+// storage when the value type can be initialized, assigned, and compared
+// with nullptr.
+//
+// For example:
+// - sizeof(fit::nullable<void*>) == sizeof(void*)
+// - sizeof(std::optional<void*>) == sizeof(struct { bool; void*; })
+// - sizeof(fit::nullable<int>) == sizeof(struct { bool; int; })
+// - sizeof(std::optional<int>) == sizeof(struct { bool; int; })
+template <typename T, bool = (is_nullable<T>::value &&
+ std::is_constructible<T, T&&>::value &&
+ std::is_assignable<T&, T&&>::value)>
+class nullable final {
+public:
+ using value_type = T;
+
+ constexpr nullable() = default;
+ explicit constexpr nullable(decltype(nullptr)) {}
+ explicit constexpr nullable(T value)
+ : opt_(std::move(value)) {}
+ nullable(const nullable& other) = default;
+ nullable(nullable&& other) = default;
+ ~nullable() = default;
+
+ constexpr T& value() & { return opt_.value(); }
+ constexpr const T& value() const& { return opt_.value(); }
+ constexpr T&& value() && { return std::move(opt_.value()); }
+ constexpr const T&& value() const&& { return std::move(opt_.value()); }
+
+ template <typename U = T>
+ constexpr T value_or(U&& default_value) const {
+ return opt_.value_or(std::forward<U>(default_value));
+ }
+
+ constexpr T* operator->() { return &*opt_; }
+ constexpr const T* operator->() const { return &*opt_; }
+ constexpr T& operator*() { return *opt_; }
+ constexpr const T& operator*() const { return *opt_; }
+
+ constexpr bool has_value() const { return opt_.has_value(); }
+ explicit constexpr operator bool() const { return has_value(); }
+
+ nullable& operator=(const nullable& other) = default;
+ nullable& operator=(nullable&& other) = default;
+
+ nullable& operator=(decltype(nullptr)) {
+ reset();
+ return *this;
+ }
+
+ nullable& operator=(T value) {
+ opt_ = std::move(value);
+ return *this;
+ }
+
+ void reset() { opt_.reset(); }
+
+ void swap(nullable& other) { opt_.swap(other.opt_); }
+
+private:
+ optional<T> opt_;
+};
+
+template <typename T>
+class nullable<T, true> final {
+public:
+ using value_type = T;
+
+ constexpr nullable()
+ : value_(nullptr) {}
+ explicit constexpr nullable(decltype(nullptr))
+ : value_(nullptr) {}
+ explicit constexpr nullable(T value)
+ : value_(std::move(value)) {}
+ nullable(const nullable& other) = default;
+ nullable(nullable&& other)
+ : value_(std::move(other.value_)) {
+ other.value_ = nullptr;
+ }
+ ~nullable() = default;
+
+ constexpr T& value() & {
+ assert(has_value());
+ return value_;
+ }
+ constexpr const T& value() const& {
+ assert(has_value());
+ return value_;
+ }
+ constexpr T&& value() && {
+ assert(has_value());
+ return std::move(value_);
+ }
+ constexpr const T&& value() const&& {
+ assert(has_value());
+ return std::move(value_);
+ }
+
+ template <typename U = T>
+ constexpr T value_or(U&& default_value) const {
+ return has_value() ? value_ : static_cast<T>(std::forward<U>(default_value));
+ }
+
+ constexpr T* operator->() { return &value_; }
+ constexpr const T* operator->() const { return &value_; }
+ constexpr T& operator*() { return value_; }
+ constexpr const T& operator*() const { return value_; }
+
+ constexpr bool has_value() const { return !(value_ == nullptr); }
+ explicit constexpr operator bool() const { return has_value(); }
+
+ nullable& operator=(const nullable& other) = default;
+ nullable& operator=(nullable&& other) {
+ if (&other == this)
+ return *this;
+ value_ = std::move(other.value_);
+ other.value_ = nullptr;
+ return *this;
+ }
+
+ nullable& operator=(decltype(nullptr)) {
+ reset();
+ return *this;
+ }
+
+ nullable& operator=(T value) {
+ value_ = std::move(value);
+ return *this;
+ }
+
+ void reset() { value_ = nullptr; }
+
+ void swap(nullable& other) {
+ using std::swap;
+ swap(value_, other.value_);
+ }
+
+private:
+ T value_;
+};
+
+template <typename T>
+void swap(nullable<T>& a, nullable<T>& b) {
+ a.swap(b);
+}
+
+template <typename T>
+constexpr bool operator==(const nullable<T>& lhs, decltype(nullptr)) {
+ return !lhs.has_value();
+}
+template <typename T>
+constexpr bool operator!=(const nullable<T>& lhs, decltype(nullptr)) {
+ return lhs.has_value();
+}
+
+template <typename T>
+constexpr bool operator==(decltype(nullptr), const nullable<T>& rhs) {
+ return !rhs.has_value();
+}
+template <typename T>
+constexpr bool operator!=(decltype(nullptr), const nullable<T>& rhs) {
+ return rhs.has_value();
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const nullable<T>& lhs, const nullable<U>& rhs) {
+ return (lhs.has_value() == rhs.has_value()) && (!lhs.has_value() || *lhs == *rhs);
+}
+template <typename T, typename U>
+constexpr bool operator!=(const nullable<T>& lhs, const nullable<U>& rhs) {
+ return (lhs.has_value() != rhs.has_value()) || (lhs.has_value() && *lhs != *rhs);
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const nullable<T>& lhs, const U& rhs) {
+ return (lhs.has_value() != is_null(rhs)) && (!lhs.has_value() || *lhs == rhs);
+}
+template <typename T, typename U>
+constexpr bool operator!=(const nullable<T>& lhs, const U& rhs) {
+ return (lhs.has_value() == is_null(rhs)) || (lhs.has_value() && *lhs != rhs);
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const T& lhs, const nullable<U>& rhs) {
+ return (is_null(lhs) != rhs.has_value()) && (!rhs.has_value() || lhs == *rhs);
+}
+template <typename T, typename U>
+constexpr bool operator!=(const T& lhs, const nullable<U>& rhs) {
+ return (is_null(lhs) == rhs.has_value()) || (rhs.has_value() && lhs != *rhs);
+}
+
+} // namespace fit
+
+#endif // LIB_FIT_NULLABLE_H_
diff --git a/pkg/fit/include/lib/fit/optional.h b/pkg/fit/include/lib/fit/optional.h
new file mode 100644
index 0000000..929f05b
--- /dev/null
+++ b/pkg/fit/include/lib/fit/optional.h
@@ -0,0 +1,298 @@
+// 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.
+
+#ifndef LIB_FIT_OPTIONAL_H_
+#define LIB_FIT_OPTIONAL_H_
+
+#include <assert.h>
+
+#include <new>
+#include <type_traits>
+#include <utility>
+
+namespace fit {
+namespace internal {
+
+template <typename T, bool = std::is_assignable<T&, const T&>::value>
+struct copy_assign_or_reconstruct final {
+ static void assign(T* dest, const T& source) {
+ dest->~T();
+ new (dest) T(source);
+ }
+};
+
+template <typename T>
+struct copy_assign_or_reconstruct<T, true> final {
+ static void assign(T* dest, const T& source) {
+ *dest = source;
+ }
+};
+
+template <typename T, bool = std::is_assignable<T&, T&&>::value>
+struct move_assign_or_reconstruct final {
+ static void assign(T* dest, T&& source) {
+ dest->~T();
+ new (dest) T(std::move(source));
+ }
+
+ static void swap(T& a, T& b) {
+ T temp(std::move(a));
+ a.~T();
+ new (&a) T(std::move(b));
+ b.~T();
+ new (&b) T(std::move(temp));
+ }
+};
+
+template <typename T>
+struct move_assign_or_reconstruct<T, true> final {
+ static void assign(T* dest, T&& source) {
+ *dest = std::move(source);
+ }
+
+ static void swap(T& a, T& b) {
+ using std::swap;
+ swap(a, b);
+ }
+};
+
+} // namespace internal
+
+// A sentinel value for |fit::optional<T>| indicating that it contains
+// no value.
+struct nullopt_t {
+ explicit constexpr nullopt_t(int) {}
+};
+static constexpr nullopt_t nullopt(0);
+
+// A minimal implementation of an |std::optional<T>| work-alike for C++ 14.
+//
+// See also |fit::nullable<T>| which may be more efficient in certain
+// circumstances if T can be initialized, assigned, and compared with
+// nullptr.
+//
+// TODO(US-90): The initial implementation only covers a minimal subset of the
+// std::optional API. Flesh this out more fully then define fit::optional
+// to be an alias for std::optional when compiling with C++ 17.
+template <typename T>
+class optional final {
+public:
+ using value_type = T;
+
+ constexpr optional()
+ : has_value_(false) {}
+ constexpr optional(nullopt_t)
+ : has_value_(false) {}
+
+ explicit constexpr optional(T value)
+ : has_value_(true), value_(std::move(value)) {}
+
+ optional(const optional& other)
+ : has_value_(other.has_value_) {
+ if (has_value_) {
+ new (&value_) T(other.value_);
+ }
+ }
+
+ optional(optional&& other)
+ : has_value_(other.has_value_) {
+ if (has_value_) {
+ new (&value_) T(std::move(other.value_));
+ other.value_.~T();
+ other.has_value_ = false;
+ }
+ }
+
+ // TODO(US-90): Presence of this destructor makes the type non-literal.
+ // We should specialize this type to handle the case where T is literal
+ // explicitly so that expressions these types can be constexpr.
+ ~optional() {
+ if (has_value_) {
+ value_.~T();
+ }
+ }
+
+ constexpr T& value() & {
+ assert(has_value_);
+ return value_;
+ }
+
+ constexpr const T& value() const& {
+ assert(has_value_);
+ return value_;
+ }
+
+ constexpr T&& value() && {
+ assert(has_value_);
+ return std::move(value_);
+ }
+
+ constexpr const T&& value() const&& {
+ assert(has_value_);
+ return std::move(value_);
+ }
+
+ template <typename U = T>
+ constexpr T value_or(U&& default_value) const {
+ return has_value_ ? value_ : static_cast<T>(std::forward<U>(default_value));
+ }
+
+ constexpr T* operator->() { return &value_; }
+ constexpr const T* operator->() const { return &value_; }
+ constexpr T& operator*() { return value_; }
+ constexpr const T& operator*() const { return value_; }
+
+ bool has_value() const { return has_value_; }
+ explicit operator bool() const { return has_value(); }
+
+ optional& operator=(const optional& other) {
+ if (&other == this)
+ return *this;
+ if (has_value_) {
+ if (other.has_value_) {
+ ::fit::internal::copy_assign_or_reconstruct<T>::assign(
+ &value_, other.value_);
+ } else {
+ reset();
+ }
+ } else if (other.has_value_) {
+ new (&value_) T(other.value_);
+ has_value_ = true;
+ }
+ return *this;
+ }
+
+ optional& operator=(optional&& other) {
+ if (&other == this)
+ return *this;
+ if (has_value_) {
+ if (other.has_value_) {
+ ::fit::internal::move_assign_or_reconstruct<T>::assign(
+ &value_, std::move(other.value_));
+ other.value_.~T();
+ other.has_value_ = false;
+ } else {
+ reset();
+ }
+ } else if (other.has_value_) {
+ new (&value_) T(std::move(other.value_));
+ has_value_ = true;
+ other.value_.~T();
+ other.has_value_ = false;
+ }
+ return *this;
+ }
+
+ optional& operator=(nullopt_t) {
+ reset();
+ return *this;
+ }
+
+ optional& operator=(T value) {
+ if (has_value_) {
+ ::fit::internal::move_assign_or_reconstruct<T>::assign(
+ &value_, std::move(value));
+ } else {
+ new (&value_) T(std::move(value));
+ has_value_ = true;
+ }
+ return *this;
+ }
+
+ void reset() {
+ if (has_value_) {
+ value_.~T();
+ has_value_ = false;
+ }
+ }
+
+ void swap(optional& other) {
+ if (&other == this)
+ return;
+ if (has_value_) {
+ if (other.has_value_) {
+ ::fit::internal::move_assign_or_reconstruct<T>::swap(
+ value_, other.value_);
+ } else {
+ new (&other.value_) T(std::move(value_));
+ other.has_value_ = true;
+ value_.~T();
+ has_value_ = false;
+ }
+ } else if (other.has_value_) {
+ new (&value_) T(std::move(other.value_));
+ has_value_ = true;
+ other.value_.~T();
+ other.has_value_ = false;
+ }
+ }
+
+ template <typename... Args>
+ T& emplace(Args&&... args) {
+ reset();
+ new (&value_) T(std::forward<Args...>(args)...);
+ has_value_ = true;
+ return value_;
+ }
+
+private:
+ bool has_value_;
+ union {
+ T value_;
+ };
+};
+
+template <typename T>
+void swap(optional<T>& a, optional<T>& b) {
+ a.swap(b);
+}
+
+template <typename T>
+constexpr bool operator==(const optional<T>& lhs, nullopt_t) {
+ return !lhs.has_value();
+}
+template <typename T>
+constexpr bool operator!=(const optional<T>& lhs, nullopt_t) {
+ return lhs.has_value();
+}
+
+template <typename T>
+constexpr bool operator==(nullopt_t, const optional<T>& rhs) {
+ return !rhs.has_value();
+}
+template <typename T>
+constexpr bool operator!=(nullopt_t, const optional<T>& rhs) {
+ return rhs.has_value();
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
+ return (lhs.has_value() == rhs.has_value()) && (!lhs.has_value() || *lhs == *rhs);
+}
+template <typename T, typename U>
+constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
+ return (lhs.has_value() != rhs.has_value()) || (lhs.has_value() && *lhs != *rhs);
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() && *lhs == rhs;
+}
+template <typename T, typename U>
+constexpr bool operator!=(const optional<T>& lhs, const U& rhs) {
+ return !lhs.has_value() || *lhs != rhs;
+}
+
+template <typename T, typename U>
+constexpr bool operator==(const T& lhs, const optional<U>& rhs) {
+ return rhs.has_value() && lhs == *rhs;
+}
+template <typename T, typename U>
+constexpr bool operator!=(const T& lhs, const optional<U>& rhs) {
+ return !rhs.has_value() || lhs != *rhs;
+}
+
+} // namespace fit
+
+#endif // LIB_FIT_OPTIONAL_H_
diff --git a/pkg/fit/include/lib/fit/promise.h b/pkg/fit/include/lib/fit/promise.h
new file mode 100644
index 0000000..ece9e75
--- /dev/null
+++ b/pkg/fit/include/lib/fit/promise.h
@@ -0,0 +1,1520 @@
+// 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.
+
+#ifndef LIB_FIT_PROMISE_H_
+#define LIB_FIT_PROMISE_H_
+
+#include <assert.h>
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "function.h"
+#include "promise_internal.h"
+#include "result.h"
+#include "variant.h"
+
+namespace fit {
+
+// A |fit::promise| is a building block for asynchronous control flow that
+// wraps an asynchronous task in the form of a "continuation" that is
+// repeatedly invoked by an executor until it produces a result.
+//
+// Additional asynchronous tasks can be chained onto the promise using
+// a variety of combinators such as |then()|.
+//
+// Use |fit::make_promise()| to create a promise.
+// Use |fit::future| to more conveniently hold a promise or its result.
+// Use |fit::pending_task| to wrap a promise as a pending task for execution.
+// Use |fit::executor| to execute a pending task.
+// See examples below.
+//
+// Always look to the future; never look back.
+//
+// SYNOPSIS
+//
+// |V| is the type of value produced when the completes successfully.
+// Defaults to |void|.
+//
+// |E| is the type of error produced when the completes with an error.
+// Defaults to |void|.
+//
+// Class members are documented in |fit::promise_impl|.
+//
+// CHAINING PROMISES USING COMBINATORS
+//
+// Promises can be chained together using combinators such as |then()|
+// which consume the original promise(s) and return a new combined promise.
+//
+// For example, the |then()| combinator returns a promise that has the effect
+// of asynchronously awaiting completion of the prior promise (the instance
+// upon which |then()| was called) then delivering its result to a handler
+// function.
+//
+// Available combinators defined in this library:
+//
+// |then()|: run a handler when prior promise completes
+// |and_then()|: run a handler when prior promise completes successfully
+// |or_else()|: run a handler when prior promise completes with an error
+// |inspect()|: examine result of prior promise
+// |discard_result()|: discard result and unconditionally return
+// fit::result<> when prior promise completes
+// |wrap_with()|: applies a wrapper to the promise
+// |box()|: wraps the promise's continuation into a |fit::function|
+// |fit::join_promises()|: await multiple promises, once they all complete
+// return a tuple of their results
+//
+// You can also create your own custom combinators by crafting new
+// types of continuations.
+//
+// CONTINUATIONS AND HANDLERS
+//
+// Internally, |fit::promise| wraps a continuation (a kind of callable
+// object) that holds the state of the asynchronous task and provides a
+// means for making progress through repeated invocation.
+//
+// A promise's continuation is generated through the use of factories
+// such as |make_promise()| and combinators such as |then()|. Most of
+// these functions accept a client-supplied "handler" (another kind
+// of callable object, often a lambda expression) which performs the actual
+// computations.
+//
+// Continuations have a very regular interface: they always accept a
+// |fit::context&| argument and return a |fit::result|. Conversely, handlers
+// have a very flexible interface: clients can provide them in many forms
+// all of which are documented by the individual functions which consume them.
+// It's pretty easy to use: the library takes care of wrapping client-supplied
+// handlers of all supported forms into the continuations it uses internally.
+//
+// THEORY OF OPERATION
+//
+// On its own, a promise is "inert"; it only makes progress in response to
+// actions taken by its owner. The state of the promise never changes
+// spontaneously or concurrently.
+//
+// Typically, a promise is executed by wrapping it into a |fit::pending_task|
+// and scheduling it for execution using |fit::executor::schedule_task()|.
+// A promise's |operator(fit::context&)| can also be invoked directly by its owner
+// from within the scope of another task (this is used to implement combinators
+// and futures) though the principle is the same.
+//
+// |fit::executor| is an abstract class that encapsulates a strategy for
+// executing tasks. The executor is responsible for invoking each tasks's
+// continuation until the task returns a non-pending result, indicating that
+// the task has been completed.
+//
+// The method of execution and scheduling of each continuation call is left
+// to the discretion of each executor implementation. Typical executor
+// implementations may dispatch tasks on an event-driven message loop or on
+// a thread pool. Developers are responsible for selecting appropriate
+// executor implementations for their programs.
+//
+// During each invocation, the executor passes the continuation an execution
+// context object represented by a subclass of |fit::context|. The continuation
+// attempts to make progress then returns a value of type |fit::result| to
+// indicate whether it completed successfully (signaled by |fit::ok()|),
+// failed with an error (signaled by |fit::error()|, or was unable to complete
+// the task during that invocation (signaled by |fit::pending()|).
+// For example, a continuation may be unable to complete the task if it must
+// asynchronously await completion of an I/O or IPC operation before it
+// can proceed any further.
+//
+// If the continuation was unable to complete the task during its invocation,
+// it may to call |fit::context::suspend_task()| to acquire a
+// |fit::suspended_task| object. The continuation then arranges for the
+// task to be resumed asynchronously (with |fit::suspended_task::resume_task()|)
+// once it becomes possible for the promise to make forward progress again.
+// Finally, the continuation returns returns |fit::pending()| to indicate to
+// the executor that it was unable to complete the task during that invocation.
+//
+// When the executor receives a pending result from a task's continuation,
+// it moves the task into a table of suspended tasks. A suspended task
+// is considered abandoned if has not been resume and all remaining
+// |fit::suspended_task| handles representing it have been dropped.
+// When a task is abandoned, the executor removes it from its table of
+// suspended tasks and destroys the task because it is not possible for the task
+// to be resumed or to make progress from that state.
+//
+// See also |fit::single_threaded_executor| for a simple executor implementation.
+//
+// BOXED AND UNBOXED PROMISES
+//
+// To make combination and execution as efficient as possible, the promises
+// returned by |fit::make_promise| and by combinators are parameterized by
+// complicated continuation types that are hard to describe, often consisting of
+// nested templates and lambdas. These are referred to as "unboxed"
+// promises. In contrast, "boxed" promises are parameterized by a
+// a |fit::function| that hides (or "erases") the type of the continuation
+// thereby yielding type that is easier to describe.
+//
+// You can recognize boxed and unboxed promises by their types.
+// Here are two examples:
+//
+// - A boxed promise type: `fit::promise<void, void>` which is an alias for
+// `fit::promise_impl<void, void, std::function<fit::result<void, void>>`.
+// - An unboxed promise type: `fit::promise_impl<void, void,
+// fit::internal::then_continuation<...something unintelligible...>>`
+//
+// Although boxed promises are easier to manipulate, they may cause the
+// continuation to be allocated on the heap. Chaining boxed promises can
+// result in multiple allocations being produced.
+//
+// Conversely, unboxed promises have full type information. Not only does
+// this defer heap allocation but it also makes it easier for the C++
+// compiler to fuse a chains of unboxed promises together into a single
+// object that is easier to optimize.
+//
+// Unboxed promises can be boxed by assigning them to a boxed promise
+// type (such as |fit::promise<>|) or using the |box()| combinator.
+//
+// As a rule of thumb, always defer boxing of promises until it is necessary
+// to transport them using a simpler type.
+//
+// Do this: (chaining as a single expression performs at most one heap allocation)
+//
+// fit::promise<> f = fit::make_promise([] { ... });
+// .then([](fit::result<> result) { ... });
+// .and_then([] { ... });
+//
+// Or this: (still only performs at most one heap allocation)
+//
+// auto f = fit::make_promise([] { ... });
+// auto g = f.then([](fit::result<> result) { ... });
+// auto h = g.and_then([] { ... });
+// fit::promise<> boxed_h = h;
+//
+// But don't do this: (incurs up to three heap allocations due to eager boxing)
+//
+// fit::promise<> f = fit::make_promise([] { ... });
+// fit::promise<> g = f.then([](fit::result<> result) { ... });
+// fit::promise<> h = g.and_then([] { ... });
+//
+// SINGLE OWNERSHIP MODEL
+//
+// Promises have single-ownership semantics. This means that there
+// can only be at most one reference to the task represented by its
+// continuation along with any state held by that continuation.
+//
+// When a combinator is applied to a promise, ownership of its continuation
+// is transferred to the combined promise, leaving the original promise
+// in an "empty" state without a continuation. Note that it is an error
+// to attempt to invoke an empty promise (will assert at runtime).
+//
+// This model greatly simplifies reasoning about object lifetime.
+// If a promise goes out of scope without completing its task, the task
+// is considered "abandoned", causing all associated state to be destroyed.
+//
+// Note that a promise may capture references to other objects whose lifetime
+// differs from that of the promise. It is the responsibility of the promise
+// to ensure reachability of the objects whose reference it captures such
+// as by using reference counted pointers, weak pointers, or other appropriate
+// mechanisms to ensure memory safety.
+//
+// THREADING MODEL
+//
+// Promise objects are not thread-safe themselves. You cannot call their
+// methods concurrently (or re-entrantly). However, promises can safely
+// be moved to other threads and executed there (unless their continuation
+// requires thread affinity for some reason but that's beyond the scope
+// of this document).
+//
+// This property of being thread-independent, combined with the single
+// ownership model, greatly simplifies the implementation of thread pool
+// based executors.
+//
+// RESULT RETENTION AND FIT::FUTURES
+//
+// A promise's continuation can only be executed to completion once.
+// After it completes, it cannot be run again.
+//
+// This method of execution is very efficient; the promise's result is returned
+// directly to its invoker; it is not copied or retained within the promise
+// object itself. It is entirely the caller's responsibility to decide how to
+// consume or retain the result if need be.
+//
+// For example, the caller can move the promise into a |fit::future| to
+// more conveniently hold either the promise or its result upon completion.
+//
+// CLARIFICATION OF NOMENCLATURE
+//
+// In this library, the words "promise" and "future" have the following
+// definitions:
+//
+// - A *promise* holds the function that performs an asynchronous task.
+// It is the means to produce a value.
+// - A *future* holds the value produced by an asynchronous task or a
+// promise to produce that value if the task has not yet completed.
+// It is a proxy for a value that is to be computed.
+//
+// Be aware that other libraries may use these terms slightly differently.
+//
+// For more information about the theory of futures and promises, see
+// https://en.wikipedia.org/wiki/Futures_and_promises.
+//
+// COMPARISON WITH STD::FUTURE
+//
+// |std::future| provides a mechanism for running asynchronous tasks
+// and awaiting their results on other threads. Waiting can be performed
+// either by blocking the waiting thread or by polling the future.
+// The manner in which tasks are scheduled and executed is entirely
+// controlled by the C++ standard library and offers limited control
+// to developers.
+//
+// |fit::promise| and |fit::future| provide a mechanism for running asynchronous
+// tasks, chaining additional tasks using combinators, and awaiting their
+// results. An executor is responsible for suspending tasks awaiting
+// results of other tasks and is at liberty to run other tasks on the
+// same thread rather than blocking. In addition, developers can create custom
+// executors to implement their own policies for running tasks.
+//
+// Decoupling awaiting from blocking makes |fit::promise| quite versatile.
+// |fit::promise| can also interoperate with other task dispatching mechanisms
+// (including |std::future|) using adapters such as |fit::bridge|.
+//
+// EXAMPLE
+//
+// - https://fuchsia.googlesource.com/zircon/+/master/system/utest/fit/examples/promise_example1.cpp
+// - https://fuchsia.googlesource.com/zircon/+/master/system/utest/fit/examples/promise_example2.cpp
+//
+template <typename V = void, typename E = void>
+using promise = promise_impl<function<result<V, E>(fit::context&)>>;
+
+// Promise implementation details.
+// See |fit::promise| documentation for more information.
+template <typename Continuation>
+class promise_impl final {
+ static_assert(
+ ::fit::internal::is_continuation<Continuation>::value,
+ "Continuation type is invalid. A continuation is a callable object "
+ "with this signature: fit::result<V, E>(fit::context&).");
+
+ using state_type = nullable<Continuation>;
+
+public:
+ // The type of callable object held by the promise.
+ // Its signature is: result_type(fit::context&).
+ using continuation_type = Continuation;
+
+ // The promise's result type.
+ // Equivalent to fit::result<value_type, error_type>.
+ using result_type = typename ::fit::internal::continuation_traits<
+ Continuation>::result_type;
+
+ // The type of value produced when the promise completes successfully.
+ // May be void.
+ using value_type = typename result_type::value_type;
+
+ // The type of value produced when the promise completes with an error.
+ // May be void.
+ using error_type = typename result_type::error_type;
+
+ // Creates an empty promise without a continuation.
+ // A continuation must be assigned before the promise can be used.
+ promise_impl() = default;
+ explicit promise_impl(decltype(nullptr)) {}
+
+ // Creates a promise with a continuation.
+ // If |continuation| equals nullptr then the promise is empty.
+ explicit promise_impl(continuation_type continuation)
+ : state_(std::move(continuation)) {}
+
+ // Converts from a promise holding a continuation that is assignable to
+ // to this promise's continuation type.
+ //
+ // This is typically used to create a promise with a boxed continuation
+ // type (such as |fit::function|) from an unboxed promise produced by
+ // |fit::make_promise| or by combinators.
+ //
+ // EXAMPLE
+ //
+ // // f is a promise_impl with a complicated unboxed type
+ // auto f = fit::make_promise([] { ... });
+ //
+ // // g wraps f's continuation
+ // fit::promise<> g = std::move(f);
+ //
+ template <typename OtherContinuation,
+ typename = std::enable_if_t<
+ std::is_constructible<continuation_type,
+ OtherContinuation&&>::value>>
+ promise_impl(promise_impl<OtherContinuation> other)
+ : state_(other.state_.has_value()
+ ? state_type(continuation_type(std::move(*other.state_)))
+ : state_type()) {}
+
+ // Creates a promise by taking the continuation from another promise,
+ // leaving the other promise empty.
+ promise_impl(promise_impl&& other) = default;
+
+ // Destroys the promise, releasing its continuation.
+ ~promise_impl() = default;
+
+ // Returns true if the promise is non-empty (has a valid continuation).
+ explicit operator bool() const { return state_.has_value(); }
+
+ // Invokes the promise's continuation.
+ //
+ // This method should be called by an executor to evaluate the promise.
+ // If the result's state is |result_state::pending| then the executor
+ // is responsible for arranging to invoke the promise's continuation
+ // again once it determines that it is possible to make progress
+ // towards completion of the promise encapsulated within the promise.
+ //
+ // Once the continuation returns a result with status |result_state::ok|
+ // or |result_state::error|, the promise is assigned an empty continuation.
+ //
+ // Asserts that the promise is non-empty.
+ result_type operator()(context& context) {
+ assert(state_.has_value());
+ result_type result = (*state_)(context);
+ if (!result.is_pending())
+ state_.reset();
+ return result;
+ }
+
+ // Takes the promise's continuation, leaving it in an empty state.
+ // Asserts that the promise is non-empty.
+ continuation_type take_continuation() {
+ assert(state_.has_value());
+ auto continuation = std::move(state_.value());
+ state_.reset();
+ return continuation;
+ }
+
+ // Assigns the promise by taking the continuation from another promise,
+ // leaving the other promise empty.
+ promise_impl& operator=(promise_impl&& other) = default;
+
+ // Discards the promise's continuation, leaving it empty.
+ promise_impl& operator=(decltype(nullptr)) {
+ state_.reset();
+ return *this;
+ }
+
+ // Assigns the promise's continuation.
+ promise_impl& operator=(continuation_type continuation) {
+ state_ = std::move(continuation);
+ return *this;
+ }
+
+ // Swaps the promises' continuations.
+ void swap(promise_impl& other) {
+ using std::swap;
+ swap(state_, other.state_);
+ }
+
+ // Returns an unboxed promise which invokes the specified handler
+ // function after this promise completes (successfully or unsuccessfully),
+ // passing its result.
+ //
+ // The received result's state is guaranteed to be either
+ // |fit::result_state::ok| or |fit::result_state::error|, never
+ // |fit::result_state::pending|.
+ //
+ // |handler| is a callable object (such as a lambda) which consumes the
+ // result of this promise and returns a new result with any value type
+ // and error type. Must not be null.
+ //
+ // The handler must return one of the following types:
+ // - void
+ // - fit::result<new_value_type, new_error_type>
+ // - fit::ok<new_value_type>
+ // - fit::error<new_error_type>
+ // - fit::pending
+ // - fit::promise<new_value_type, new_error_type>
+ // - any callable or unboxed promise with the following signature:
+ // fit::result<new_value_type, new_error_type>(fit::context&)
+ //
+ // The handler must accept one of the following argument lists:
+ // - (result_type)
+ // - (result_type&)
+ // - (const result_type&)
+ // - (fit::context&, result_type)
+ // - (fit::context&, result_type&)
+ // - (fit::context&, const result_type&)
+ //
+ // Asserts that the promise is non-empty.
+ // This method consumes the promise's continuation, leaving it empty.
+ //
+ // EXAMPLE
+ //
+ // auto f = fit::make_promise(...)
+ // .then([] (fit::result<int, std::string> result)
+ // -> fit::result<std::string, void> {
+ // if (result.is_ok()) {
+ // printf("received value: %d\n", result.value());
+ // if (result.value() % 15 == 0)
+ // return ::fit::ok("fizzbuzz");
+ // if (result.value() % 3 == 0)
+ // return ::fit::ok("fizz");
+ // if (result.value() % 5 == 0)
+ // return ::fit::ok("buzz");
+ // return ::fit::ok(std::to_string(result.value()));
+ // } else {
+ // printf("received error: %s\n", result.error().c_str());
+ // return ::fit::error();
+ // }
+ // })
+ // .then(...);
+ //
+ template <typename ResultHandler>
+ promise_impl<::fit::internal::then_continuation<promise_impl, ResultHandler>>
+ then(ResultHandler handler) {
+ static_assert(is_callable<ResultHandler>::value,
+ "ResultHandler must be a callable object.");
+
+ assert(!is_null(handler));
+ assert(state_.has_value());
+ return make_promise_with_continuation(
+ ::fit::internal::then_continuation<promise_impl, ResultHandler>(
+ std::move(*this), std::move(handler)));
+ }
+
+ // Returns an unboxed promise which invokes the specified handler
+ // function after this promise completes successfully, passing its
+ // resulting value.
+ //
+ // |handler| is a callable object (such as a lambda) which consumes the
+ // result of this promise and returns a new result with any value type
+ // but the same error type. Must not be null.
+ //
+ // The handler must return one of the following types:
+ // - void
+ // - fit::result<new_value_type, error_type>
+ // - fit::ok<new_value_type>
+ // - fit::error<error_type>
+ // - fit::pending
+ // - fit::promise<new_value_type, error_type>
+ // - any callable or unboxed promise with the following signature:
+ // fit::result<new_value_type, error_type>(fit::context&)
+ //
+ // The handler must accept one of the following argument lists:
+ // - (value_type)
+ // - (value_type&)
+ // - (const value_type&)
+ // - (fit::context&, value_type)
+ // - (fit::context&, value_type&)
+ // - (fit::context&, const value_type&)
+ //
+ // Asserts that the promise is non-empty.
+ // This method consumes the promise's continuation, leaving it empty.
+ //
+ // EXAMPLE
+ //
+ // auto f = fit::make_promise(...)
+ // .and_then([] (int value) {
+ // printf("received value: %d\n", value);
+ // if (value % 15 == 0)
+ // return ::fit::ok("fizzbuzz");
+ // if (value % 3 == 0)
+ // return ::fit::ok("fizz");
+ // if (value % 5 == 0)
+ // return ::fit::ok("buzz");
+ // return ::fit::ok(std::to_string(value));
+ // })
+ // .then(...);
+ //
+ template <typename ValueHandler>
+ promise_impl<::fit::internal::and_then_continuation<promise_impl, ValueHandler>>
+ and_then(ValueHandler handler) {
+ static_assert(is_callable<ValueHandler>::value,
+ "ValueHandler must be a callable object.");
+
+ assert(!is_null(handler));
+ assert(state_.has_value());
+ return make_promise_with_continuation(
+ ::fit::internal::and_then_continuation<promise_impl, ValueHandler>(
+ std::move(*this), std::move(handler)));
+ }
+
+ // Returns an unboxed promise which invokes the specified handler
+ // function after this promise completes with an error, passing its
+ // resulting error.
+ //
+ // |handler| is a callable object (such as a lambda) which consumes the
+ // result of this promise and returns a new result with any error type
+ // but the same value type. Must not be null.
+ //
+ // The handler must return one of the following types:
+ // - void
+ // - fit::result<value_type, new_error_type>
+ // - fit::ok<value_type>
+ // - fit::error<new_error_type>
+ // - fit::pending
+ // - fit::promise<value_type, new_error_type>
+ // - any callable or unboxed promise with the following signature:
+ // fit::result<value_type, new_error_type>(fit::context&)
+ //
+ // The handler must accept one of the following argument lists:
+ // - (error_type)
+ // - (error_type&)
+ // - (const error_type&)
+ // - (fit::context&, error_type)
+ // - (fit::context&, error_type&)
+ // - (fit::context&, const error_type&)
+ //
+ // Asserts that the promise is non-empty.
+ // This method consumes the promise's continuation, leaving it empty.
+ //
+ // EXAMPLE
+ //
+ // auto f = fit::make_promise(...)
+ // .or_else([] (std::string error) {
+ // printf("received error: %s\n", error.c_str());
+ // return ::fit::error();
+ // })
+ // .then(...);
+ //
+ template <typename ErrorHandler>
+ promise_impl<::fit::internal::or_else_continuation<promise_impl, ErrorHandler>>
+ or_else(ErrorHandler handler) {
+ static_assert(is_callable<ErrorHandler>::value,
+ "ErrorHandler must be a callable object.");
+
+ assert(!is_null(handler));
+ assert(state_.has_value());
+ return make_promise_with_continuation(
+ ::fit::internal::or_else_continuation<promise_impl, ErrorHandler>(
+ std::move(*this), std::move(handler)));
+ }
+
+ // Returns an unboxed promise which invokes the specified handler
+ // function after this promise completes (successfully or unsuccessfully),
+ // passing it the promise's result then delivering the result onwards
+ // to the next promise once the handler returns.
+ //
+ // The handler receive a copy, const reference, or non-const reference
+ // depending on the signature of the handler's last argument.
+ //
+ // - Copies and const references are especially useful for inspecting a
+ // result mid-stream without modification, such as printing it for
+ // debugging.
+ // - Non-const references are especially useful for synchronously
+ // modifying a result mid-stream, such as clamping its bounds or
+ // injecting a default value.
+ //
+ // |handler| is a callable object (such as a lambda) which can examine
+ // or modify the incoming result. Unlike |then()|, the handler does
+ // not need to propagate the result onwards. Must not be null.
+ //
+ // The handler must return one of the following types:
+ // - void
+ //
+ // The handler must accept one of the following argument lists:
+ // - (result_type)
+ // - (result_type&)
+ // - (const result_type&)
+ // - (fit::context&, result_type)
+ // - (fit::context&, result_type&)
+ // - (fit::context&, const result_type&)
+ //
+ // Asserts that the promise is non-empty.
+ // This method consumes the promise's continuation, leaving it empty.
+ //
+ // EXAMPLE
+ //
+ // auto f = fit::make_promise(...)
+ // .inspect([] (const fit::result<int, std::string>& result) {
+ // if (result.is_ok())
+ // printf("received value: %d\n", result.value());
+ // else
+ // printf("received error: %s\n", result.error().c_str());
+ // })
+ // .then(...);
+ //
+ template <typename InspectHandler>
+ promise_impl<::fit::internal::inspect_continuation<promise_impl, InspectHandler>>
+ inspect(InspectHandler handler) {
+ static_assert(is_callable<InspectHandler>::value,
+ "InspectHandler must be a callable object.");
+ static_assert(std::is_void<typename callable_traits<
+ InspectHandler>::return_type>::value,
+ "InspectHandler must return void.");
+
+ assert(!is_null(handler));
+ assert(state_.has_value());
+ return make_promise_with_continuation(
+ ::fit::internal::inspect_continuation<promise_impl, InspectHandler>(
+ std::move(*this), std::move(handler)));
+ }
+
+ // Returns an unboxed promise which discards the result of this promise
+ // once it completes, thereby always producing a successful result of
+ // type fit::result<void, void> regardless of whether this promise
+ // succeeded or failed.
+ //
+ // Asserts that the promise is non-empty.
+ // This method consumes the promise's continuation, leaving it empty.
+ //
+ // EXAMPLE
+ //
+ // auto f = fit::make_promise(...)
+ // .discard_result()
+ // .then(...);
+ //
+ promise_impl<::fit::internal::discard_result_continuation<promise_impl>>
+ discard_result() {
+ assert(state_.has_value());
+ return make_promise_with_continuation(
+ ::fit::internal::discard_result_continuation<promise_impl>(
+ std::move(*this)));
+ }
+
+ // Applies a |wrapper| to the promise. Invokes the wrapper's |wrap()|
+ // method, passes the promise to the wrapper by value followed by any
+ // additional |args| passed to |wrap_with()|, then returns the wrapper's
+ // result.
+ //
+ // |Wrapper| is a type that implements a method called |wrap()| which
+ // accepts a promise as its argument and produces a wrapped result of
+ // any type, such as another promise.
+ //
+ // Asserts that the promise is non-empty.
+ // This method consumes the promise's continuation, leaving it empty.
+ //
+ // EXAMPLE
+ //
+ // In this example, |fit::sequencer| is a wrapper type that imposes
+ // FIFO execution order onto a sequence of wrapped promises.
+ //
+ // // This wrapper type is intended to be applied to
+ // // a sequence of promises so we store it in a variable.
+ // fit::sequencer seq;
+ //
+ // // This task consists of some amount of work that must be
+ // // completed sequentially followed by other work that can
+ // // happen in any order. We use |wrap_with()| to wrap the
+ // // sequential work with the sequencer.
+ // fit::promise<> perform_complex_task() {
+ // return fit::make_promise([] { /* do sequential work */ })
+ // .then([] (fit::result<> result) { /* this will also be wrapped */ })
+ // .wrap_with(seq)
+ // .then([] (fit::result<> result) { /* do more work */ });
+ // }
+ //
+ // This example can also be written without using |wrap_with()|.
+ // The behavior is equivalent but the syntax may seem more awkward.
+ //
+ // fit::sequencer seq;
+ //
+ // promise<> perform_complex_task() {
+ // return seq.wrap(
+ // fit::make_promise([] { /* sequential work */ })
+ // ).then([] (fit::result<> result) { /* more work */ });
+ // }
+ //
+ template <typename Wrapper, typename... Args>
+ decltype(auto) wrap_with(Wrapper& wrapper, Args... args) {
+ assert(state_.has_value());
+ return wrapper.wrap(std::move(*this),
+ std::forward<Args>(args)...);
+ }
+
+ // Wraps the promise's continuation into a |fit::function|.
+ //
+ // A boxed promise is easier to store and pass around than the unboxed
+ // promises produced by |fit::make_promise()| and combinators, though boxing
+ // may incur a heap allocation.
+ //
+ // It is a good idea to defer boxing the promise until after all
+ // desired combinators have been applied to prevent unnecessary heap
+ // allocation during intermediate states of the promise's construction.
+ //
+ // Returns an empty promise if this promise is empty.
+ // This method consumes the promise's continuation, leaving it empty.
+ //
+ // EXAMPLE
+ //
+ // // f's is a fit::promise_impl<> whose continuation contains an
+ // // anonymous type (the lambda)
+ // auto f = fit::make_promise([] {});
+ //
+ // // g's type will be fit::promise<> due to boxing
+ // auto boxed_f = f.box();
+ //
+ // // alternately, we can get exactly the same effect by assigning
+ // // the unboxed promise to a variable of a named type instead of
+ // // calling box()
+ // fit::promise<> boxed_f = std::move(f);
+ //
+ promise_impl<function<result_type(context&)>> box() {
+ return std::move(*this);
+ }
+
+ promise_impl(const promise_impl&) = delete;
+ promise_impl& operator=(const promise_impl&) = delete;
+
+private:
+ template <typename>
+ friend class promise_impl;
+
+ state_type state_;
+};
+
+template <typename Continuation>
+void swap(promise_impl<Continuation>& a,
+ promise_impl<Continuation>& b) {
+ a.swap(b);
+}
+
+template <typename Continuation>
+bool operator==(const promise_impl<Continuation>& f,
+ decltype(nullptr)) {
+ return !f;
+}
+template <typename Continuation>
+bool operator==(decltype(nullptr),
+ const promise_impl<Continuation>& f) {
+ return !f;
+}
+template <typename Continuation>
+bool operator!=(const promise_impl<Continuation>& f,
+ decltype(nullptr)) {
+ return !!f;
+}
+template <typename Continuation>
+bool operator!=(decltype(nullptr),
+ const promise_impl<Continuation>& f) {
+ return !!f;
+}
+
+// Makes a promise containing the specified continuation.
+//
+// This function is used for making a promises given a callable object
+// that represents a valid continuation type. In contrast,
+// |fit::make_promise()| supports a wider range of types and should be
+// preferred in most situations.
+//
+// |Continuation| is a callable object with the signature
+// fit::result<V, E>(fit::context&).
+template <typename Continuation>
+inline promise_impl<Continuation> make_promise_with_continuation(
+ Continuation continuation) {
+ return promise_impl<Continuation>(std::move(continuation));
+}
+
+// Returns an unboxed promise that wraps the specified handler.
+// The type of the promise's result is inferred from the handler's result.
+//
+// |handler| is a callable object (such as a lambda. Must not be null.
+//
+// The handler must return one of the following types:
+// - void
+// - fit::result<value_type, error_type>
+// - fit::ok<value_type>
+// - fit::error<error_type>
+// - fit::pending
+// - fit::promise<value_type, error_type>
+// - any callable or unboxed promise with the following signature:
+// fit::result<value_type, error_type>(fit::context&)
+//
+// The handler must accept one of the following argument lists:
+// - ()
+// - (fit::context&)
+//
+// See documentation of |fit::promise| for more information.
+//
+// SYNOPSIS
+//
+// |Handler| is the handler function type. It is typically inferred by the
+// compiler from the |handler| argument.
+//
+// EXAMPLE
+//
+// enum class weather_type { sunny, glorious, cloudy, eerie, ... };
+//
+// weather_type look_outside() { ... }
+// void wait_for_tomorrow(fit::suspended_task task) {
+// ... arrange to call task.resume_task() tomorrow ...
+// }
+//
+// fit::promise<weather_type, std::string> wait_for_good_weather(int max_days) {
+// return fit::make_promise([days_left = max_days] (fit::context context&) mutable
+// -> fit::result<int, std::string> {
+// weather_type weather = look_outside();
+// if (weather == weather_type::sunny || weather == weather_type::glorious)
+// return fit::ok(weather);
+// if (days_left > 0) {
+// wait_for_tomorrow(context.suspend_task());
+// return fit::pending();
+// }
+// days_left--;
+// return fit::error("nothing but grey skies");
+// });
+// }
+//
+// auto f = wait_for_good_weather(7)
+// .and_then([] (weather_type weather) { ... })
+// .or_else([] (std::string error) { ... });
+//
+template <typename PromiseHandler>
+inline promise_impl<::fit::internal::context_handler_invoker<PromiseHandler>>
+make_promise(PromiseHandler handler) {
+ static_assert(is_callable<PromiseHandler>::value,
+ "PromiseHandler must be a callable object.");
+
+ assert(!is_null(handler));
+ return make_promise_with_continuation(
+ ::fit::internal::promise_continuation<PromiseHandler>(
+ std::move(handler)));
+}
+
+// Jointly evaluates zero or more promises.
+// Returns a promise that produces a std::tuple<> containing the result
+// of each promise once they all complete.
+//
+// EXAMPLE
+//
+// auto get_random_number() {
+// return fit::make_promise([] { return rand() % 10 });
+// }
+//
+// auto get_random_product() {
+// auto f = get_random_number();
+// auto g = get_random_number();
+// return fit::join_promises(std::move(f), std::move(g))
+// .and_then([] (std::tuple<fit::result<int>, fit::result<int>> results) {
+// return fit::ok(results.get<0>.value() + results.get<1>.value());
+// });
+// }
+//
+template <typename... Promises>
+inline promise_impl<::fit::internal::join_continuation<Promises...>>
+join_promises(Promises... promises) {
+ return make_promise_with_continuation(
+ ::fit::internal::join_continuation<Promises...>(std::move(promises)...));
+}
+
+// Describes the status of a future.
+enum class future_state {
+ // The future neither holds a result nor a promise that could produce a result.
+ // An empty future cannot make progress until a promise or result is assigned to it.
+ empty,
+ // The future holds a promise that may eventually produce a result but
+ // it currently doesn't have a result. The future's promise must be
+ // invoked in order to make progress from this state.
+ pending,
+ // The future holds a successful result.
+ ok,
+ // The future holds an error result.
+ error
+};
+
+// A |fit::future| holds onto a |fit::promise| until it has completed then
+// provides access to its |fit::result|.
+//
+// SYNOPSIS
+//
+// |V| is the type of value produced when the completes successfully.
+// Defaults to |void|.
+//
+// |E| is the type of error produced when the completes with an error.
+// Defaults to |void|.
+//
+// THEORY OF OPERATION
+//
+// A future has a single owner who is responsible for setting its promise
+// or result and driving its execution. Unlike |fit::promise|, a future retains
+// the result produced by completion of its asynchronous task. Result retention
+// eases the implementation of combined tasks that need to await the results
+// of other tasks before proceeding.
+//
+// See the example for details.
+//
+// A future can be in one of four states, depending on whether it holds...
+// - a successful result: |fit::future_state::ok|
+// - an error result: |fit::future_state::error|
+// - a promise that may eventually produce a result: |fit::future_state::pending|
+// - neither: |fit::future_state_empty|
+//
+// On its own, a future is "inert"; it only makes progress in response to
+// actions taken by its owner. The state of the future never changes
+// spontaneously or concurrently.
+//
+// When the future's state is |fit::future_state::empty|, its owner is
+// responsible for setting the future's promise or result thereby moving the
+// future into the pending or ready state.
+//
+// When the future's state is |fit::future_state::pending|, its owner is
+// responsible for calling the future's |operator()| to invoke the promise.
+// If the promise completes and returns a result, the future will transition
+// to the ok or error state according to the result. The promise itself will
+// then be destroyed since it has fulfilled its purpose.
+//
+// When the future's state is |fit::future_state::ok|, its owner is responsible
+// for consuming the stored value using |value()|, |take_value()|,
+// |result()|, |take_result()|, or |take_ok_result()|.
+//
+// When the future's state is |fit::future_state::error|, its owner is
+// responsible for consuming the stored error using |error()|, |take_error()|,
+// |result()|, |take_result()|, or |take_error_result()|.
+//
+// See also |fit::promise| for more information about promises and their
+// execution.
+//
+// EXAMPLE
+//
+// - https://fuchsia.googlesource.com/zircon/+/master/system/utest/fit/examples/promise_example2.cpp
+template <typename V = void, typename E = void>
+using future = future_impl<promise<V, E>>;
+
+// Future implementation details.
+// See |fit::future| documentation for more information.
+template <typename Promise>
+class future_impl final {
+public:
+ // The type of promise held by the future.
+ using promise_type = Promise;
+
+ // The promise's result type.
+ // Equivalent to fit::result<value_type, error_type>.
+ using result_type = typename Promise::result_type;
+
+ // The type of value produced when the promise completes successfully.
+ // May be void.
+ using value_type = typename Promise::value_type;
+
+ // The type of value produced when the promise completes with an error.
+ // May be void.
+ using error_type = typename Promise::error_type;
+
+ // Creates a future in the empty state.
+ future_impl() = default;
+ future_impl(decltype(nullptr)) {}
+
+ // Creates a future and assigns a promise to compute its result.
+ // If the promise is empty, the future enters the empty state.
+ // Otherwise the future enters the pending state.
+ explicit future_impl(promise_type promise) {
+ if (promise) {
+ state_.template emplace<1>(std::move(promise));
+ }
+ }
+
+ // Creates a future and assigns its result.
+ // If the result is pending, the future enters the empty state.
+ // Otherwise the future enters the ok or error state.
+ explicit future_impl(result_type result) {
+ if (result) {
+ state_.template emplace<2>(std::move(result));
+ }
+ }
+
+ // Moves from another future, leaving the other one in an empty state.
+ future_impl(future_impl&& other)
+ : state_(std::move(other.state_)) {
+ other.state_.template emplace<0>();
+ }
+
+ // Destroys the promise, releasing its promise and result (if any).
+ ~future_impl() = default;
+
+ // Returns the state of the future: empty, pending, ok, or error.
+ future_state state() const {
+ switch (state_.index()) {
+ case 0:
+ return future_state::empty;
+ case 1:
+ return future_state::pending;
+ case 2:
+ return state_.template get<2>().is_ok()
+ ? future_state::ok
+ : future_state::error;
+ }
+ __builtin_unreachable();
+ }
+
+ // Returns true if the future's state is not |fit::future_state::empty|:
+ // it either holds a result or holds a promise that can be invoked to make
+ // progress towards obtaining a result.
+ explicit operator bool() const { return !is_empty(); }
+
+ // Returns true if the future's state is |fit::future_state::empty|:
+ // it does not hold a result or a promise so it cannot make progress.
+ bool is_empty() const { return state() == fit::future_state::empty; }
+
+ // Returns true if the future's state is |fit::future_state::pending|:
+ // it does not hold a result yet but it does hold a promise that can be invoked
+ // to make progress towards obtaining a result.
+ bool is_pending() const { return state() == fit::future_state::pending; }
+
+ // Returns true if the future's state is |fit::future_state::ok|:
+ // it holds a value that can be retrieved using |value()|, |take_value()|,
+ // |result()|, |take_result()|, or |take_ok_result()|.
+ bool is_ok() const { return state() == fit::future_state::ok; }
+
+ // Returns true if the future's state is |fit::future_state::error|:
+ // it holds an error that can be retrieved using |error()|, |take_error()|,
+ // |result()|, |take_result()|, or |take_error_result()|.
+ bool is_error() const { return state() == fit::future_state::error; }
+
+ // Returns true if the future's state is either |fit::future_state::ok| or
+ // |fit::future_state::error|.
+ bool is_ready() const { return state_.index() == 2; }
+
+ // Evaluates the future and returns true if its result is ready.
+ // Asserts that the future is not empty.
+ //
+ // If the promise completes and returns a result, the future will transition
+ // to the ok or error state according to the result. The promise itself will
+ // then be destroyed since it has fulfilled its purpose.
+ bool operator()(fit::context& context) {
+ switch (state_.index()) {
+ case 0:
+ return false;
+ case 1: {
+ result_type result = state_.template get<1>()(context);
+ if (!result)
+ return false;
+ state_.template emplace<2>(std::move(result));
+ return true;
+ }
+ case 2:
+ return true;
+ }
+ __builtin_unreachable();
+ }
+
+ // Gets a reference to the future's promise.
+ // Asserts that the future's state is |fit::future_state::pending|.
+ const promise_type& promise() const {
+ assert(is_pending());
+ return state_.template get<1>();
+ }
+
+ // Takes the future's promise, leaving it in an empty state.
+ // Asserts that the future's state is |fit::future_state::pending|.
+ promise_type take_promise() {
+ assert(is_pending());
+ auto promise = std::move(state_.template get<1>());
+ state_.template emplace<0>();
+ return promise;
+ }
+
+ // Gets a reference to the future's result.
+ // Asserts that the future's state is |fit::future_state::ok| or
+ // |fit::future_state::error|.
+ result_type& result() {
+ assert(is_ready());
+ return state_.template get<2>();
+ }
+ const result_type& result() const {
+ assert(is_ready());
+ return state_.template get<2>();
+ }
+
+ // Takes the future's result, leaving it in an empty state.
+ // Asserts that the future's state is |fit::future_state::ok| or
+ // |fit::future_state::error|.
+ result_type take_result() {
+ assert(is_ready());
+ auto result = std::move(state_.template get<2>());
+ state_.template emplace<0>();
+ return result;
+ }
+
+ // Gets a reference to the future's value.
+ // Asserts that the future's state is |fit::future_state::ok|.
+ template <typename R = value_type,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ R& value() {
+ assert(is_ok());
+ return state_.template get<2>().value();
+ }
+ template <typename R = value_type,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ const R& value() const {
+ assert(is_ok());
+ return state_.template get<2>().value();
+ }
+
+ // Takes the future's value, leaving it in an empty state.
+ // Asserts that the future's state is |fit::future_state::ok|.
+ template <typename R = value_type,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ R take_value() {
+ assert(is_ok());
+ auto value = state_.template get<2>().take_value();
+ state_.template emplace<0>();
+ return value;
+ }
+ ok_result<value_type> take_ok_result() {
+ assert(is_ok());
+ auto result = state_.template get<2>().take_ok_result();
+ state_.template emplace<0>();
+ return result;
+ }
+
+ // Gets a reference to the future's error.
+ // Asserts that the future's state is |fit::future_state::error|.
+ template <typename R = error_type,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ R& error() {
+ assert(is_error());
+ return state_.template get<2>().error();
+ }
+ template <typename R = error_type,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ const R& error() const {
+ assert(is_error());
+ return state_.template get<2>().error();
+ }
+
+ // Takes the future's error, leaving it in an empty state.
+ // Asserts that the future's state is |fit::future_state::error|.
+ template <typename R = error_type,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ R take_error() {
+ assert(is_error());
+ auto error = state_.template get<2>().take_error();
+ state_.template emplace<0>();
+ return error;
+ }
+ error_result<error_type> take_error_result() {
+ assert(is_error());
+ auto result = state_.template get<2>().take_error_result();
+ state_.template emplace<0>();
+ return result;
+ }
+
+ // Move assigns from another future, leaving the other one in an empty state.
+ future_impl& operator=(future_impl&& other) = default;
+
+ // Discards the future's promise and result, leaving it empty.
+ future_impl& operator=(decltype(nullptr)) {
+ state_.template emplace<0>();
+ return *this;
+ }
+
+ // Assigns a promise to compute the future's result.
+ // If the promise is empty, the future enters the empty state.
+ // Otherwise the future enters the pending state.
+ future_impl& operator=(promise_type promise) {
+ if (promise) {
+ state_.template emplace<1>(std::move(promise));
+ } else {
+ state_.template emplace<0>();
+ }
+ return *this;
+ }
+
+ // Assigns the future's result.
+ // If the result is pending, the future enters the empty state.
+ // Otherwise the future enters the ok or error state.
+ future_impl& operator=(result_type result) {
+ if (result) {
+ state_.template emplace<2>(std::move(result));
+ } else {
+ state_.template emplace<0>();
+ }
+ return *this;
+ }
+
+ // Swaps the futures' contents.
+ void swap(future_impl& other) {
+ using std::swap;
+ swap(state_, other.state_);
+ }
+
+ future_impl(const future_impl&) = delete;
+ future_impl& operator=(const future_impl&) = delete;
+
+private:
+ ::fit::internal::variant<::fit::internal::monostate,
+ promise_type, result_type>
+ state_;
+};
+
+template <typename Promise>
+void swap(future_impl<Promise>& a, future_impl<Promise>& b) {
+ a.swap(b);
+}
+
+template <typename Promise>
+bool operator==(const future_impl<Promise>& f, decltype(nullptr)) {
+ return !f;
+}
+template <typename Promise>
+bool operator==(decltype(nullptr), const future_impl<Promise>& f) {
+ return !f;
+}
+template <typename Promise>
+bool operator!=(const future_impl<Promise>& f, decltype(nullptr)) {
+ return !!f;
+}
+template <typename Promise>
+bool operator!=(decltype(nullptr), const future_impl<Promise>& f) {
+ return !!f;
+}
+
+// Makes a future containing the specified promise.
+template <typename Promise>
+future_impl<Promise> make_future(Promise promise) {
+ return future_impl<Promise>(std::move(promise));
+}
+
+// A pending task holds a |fit::promise| that can be scheduled to run on
+// a |fit::executor| using |fit::executor::schedule_task()|.
+//
+// An executor repeatedly invokes a pending task until it returns true,
+// indicating completion. Note that the promise's resulting value or error
+// is discarded since it is not meaningful to the executor. If you need
+// to consume the result, use a combinator such as |fit::pending::then()|
+// to capture it prior to wrapping the promise into a pending task.
+//
+// See documentation of |fit::promise| for more information.
+class pending_task final {
+public:
+ // The type of promise held by this task.
+ using promise_type = promise<void, void>;
+
+ // Creates an empty pending task without a promise.
+ pending_task() = default;
+
+ // Creates a pending task that wraps an already boxed promise that returns
+ // |fit::result<void, void>|.
+ pending_task(promise_type promise)
+ : promise_(std::move(promise)) {}
+
+ // Creates a pending task that wraps any kind of promise, boxed or unboxed,
+ // regardless of its result type and with any context that is assignable
+ // from this task's context type.
+ template <typename Continuation>
+ pending_task(promise_impl<Continuation> promise)
+ : promise_(promise ? promise.discard_result().box() : promise_type()) {}
+
+ pending_task(pending_task&&) = default;
+ pending_task& operator=(pending_task&&) = default;
+
+ // Destroys the pending task, releasing its promise.
+ ~pending_task() = default;
+
+ // Returns true if the pending task is non-empty (has a valid promise).
+ explicit operator bool() const { return !!promise_; }
+
+ // Evaluates the pending task.
+ // If the task completes (returns a non-pending result), the task reverts
+ // to an empty state (because the promise it holds has reverted to an empty
+ // state) and returns true.
+ // It is an error to invoke this method if the pending task is empty.
+ bool operator()(fit::context& context) {
+ return !promise_(context).is_pending();
+ }
+
+ // Extracts the pending task's promise.
+ promise_type take_promise() {
+ return std::move(promise_);
+ }
+
+ pending_task(const pending_task&) = delete;
+ pending_task& operator=(const pending_task&) = delete;
+
+private:
+ promise_type promise_;
+};
+
+// Execution context for an asynchronous task, such as a |fit::promise|,
+// |fit::future|, or |fit::pending_task|.
+//
+// When a |fit::executor| executes a task, it provides the task with an
+// execution context which enables the task to communicate with the
+// executor and manage its own lifecycle. Specialized executors may subclass
+// |fit::context| and offer additional methods beyond those which are
+// defined here, such as to provide access to platform-specific features
+// supported by the executor.
+//
+// The context provided to a task is only valid within the scope of a single
+// invocation; the task must not retain a reference to the context across
+// invocations.
+//
+// See documentation of |fit::promise| for more information.
+class context {
+public:
+ // Gets the executor that is running the task, never null.
+ virtual class executor* executor() const = 0;
+
+ // Obtains a handle that can be used to resume the task after it has been
+ // suspended.
+ //
+ // Clients should call this method before returning |fit::pending()| from
+ // the task. See documentation on |fit::executor|.
+ virtual suspended_task suspend_task() = 0;
+
+ // Converts this context to a derived context type.
+ template <typename Context,
+ typename = std::enable_if_t<std::is_base_of<context, Context>::value>>
+ Context& as() & {
+ // TODO(CP-163): We should perform a run-time type check here rather
+ // than blindly casting. That's why this method exists.
+ return static_cast<Context&>(*this);
+ }
+
+protected:
+ virtual ~context() = default;
+};
+
+// An abstract interface for executing asynchronous tasks, such as promises,
+// represented by |fit::pending_task|.
+//
+// EXECUTING TASKS
+//
+// An executor evaluates its tasks incrementally. During each iteration
+// of the executor's main loop, it invokes the next task from its ready queue.
+//
+// If the task returns true, then the task is deemed to have completed.
+// The executor removes the tasks from its queue and destroys it since there
+// it nothing left to do.
+//
+// If the task returns false, then the task is deemed to have voluntarily
+// suspended itself pending some event that it is awaiting. Prior to
+// returning, the task should acquire at least one |fit::suspended_task|
+// handle from its execution context using |fit::context::suspend_task()|
+// to provide a means for the task to be resumed once it can make forward
+// progress again.
+//
+// Once the suspended task is resumed with |fit::suspended_task::resume()|, it
+// is moved back to the ready queue and it will be invoked again during a later
+// iteration of the executor's loop.
+//
+// If all |fit::suspended_task| handles for a given task are destroyed without
+// the task ever being resumed then the task is also destroyed since there
+// would be no way for the task to be resumed from suspension. We say that
+// such a task has been "abandoned".
+//
+// The executor retains single-ownership of all active and suspended tasks.
+// When the executor is destroyed, all of its remaining tasks are also
+// destroyed.
+//
+// Please read |fit::promise| for a more detailed explanation of the
+// responsibilities of tasks and executors.
+//
+// NOTES FOR IMPLEMENTORS
+//
+// This interface is designed to support a variety of different executor
+// implementations. For example, one implementation might run its tasks on
+// a single thread whereas another might dispatch them on an event-driven
+// message loop or use a thread pool.
+//
+// See also |fit::single_threaded_executor| for a concrete implementation.
+class executor {
+public:
+ // Destroys the executor along with all of its remaining scheduled tasks
+ // that have yet to complete.
+ virtual ~executor() = default;
+
+ // Schedules a task for eventual execution by the executor.
+ //
+ // This method is thread-safe.
+ virtual void schedule_task(pending_task task) = 0;
+};
+
+// Represents a task that is awaiting resumption.
+//
+// This object has RAII semantics. If the task is not resumed by at least
+// one holder of its |suspended_task| handles, then it will be destroyed
+// by the executor since it is no longer possible for the task to make
+// progress. The task is said have been "abandoned".
+//
+// See documentation of |fit::executor| for more information.
+class suspended_task final {
+public:
+ // A handle that grants the capability to resume a suspended task.
+ // Each issued ticket must be individually resolved.
+ using ticket = uint64_t;
+
+ // The resolver mechanism implements a lightweight form of reference
+ // counting for tasks that have been suspended.
+ //
+ // When a suspended task is created in a non-empty state, it receives
+ // a pointer to a resolver interface and a ticket. The ticket is
+ // a one-time-use handle that represents the task that was suspended
+ // and provides a means to resume it. The |suspended_task| class ensures
+ // that every ticket is precisely accounted for.
+ //
+ // When |suspended_task::resume_task()| is called on an instance with
+ // a valid ticket, the resolver's |resolve_ticket()| method is invoked
+ // passing the ticket's value along with *true* to resume the task. This
+ // operation consumes the ticket so the |suspended_task| transitions to
+ // an empty state. The ticket and resolver cannot be used again by
+ // this |suspended_task| instance.
+ //
+ // Similarly, when |suspended_task::reset()| is called on an instance with
+ // a valid ticket or when the task goes out of scope on such an instance,
+ // the resolver's |resolve_ticket()| method is invoked but this time passes
+ // *false* to not resume the task. As before, the ticket is consumed.
+ //
+ // Finally, when the |suspended_task| is copied, its ticket is duplicated
+ // using |duplicate_ticket()| resulting in two tickets, both of which
+ // must be individually resolved.
+ //
+ // Resuming a task that has already been resumed has no effect.
+ // Conversely, a task is considered "abandoned" if all of its tickets
+ // have been resolved without it ever being resumed. See documentation
+ // of |fit::promise| for more information.
+ //
+ // The methods of this class are safe to call from any thread, including
+ // threads that may not be managed by the task's executor.
+ class resolver {
+ public:
+ // Duplicates the provided ticket, returning a new ticket.
+ // Note: The new ticket may have the same numeric value as the
+ // original ticket but should be considered a distinct instance
+ // that must be separately resolved.
+ virtual ticket duplicate_ticket(ticket ticket) = 0;
+
+ // Consumes the provided ticket, optionally resuming its associated task.
+ // The provided ticket must not be used again.
+ virtual void resolve_ticket(ticket ticket, bool resume_task) = 0;
+
+ protected:
+ virtual ~resolver() = default;
+ };
+
+ suspended_task()
+ : resolver_(nullptr), ticket_(0) {}
+
+ suspended_task(resolver* resolver, ticket ticket)
+ : resolver_(resolver), ticket_(ticket) {}
+
+ suspended_task(const suspended_task& other);
+ suspended_task(suspended_task&& other);
+
+ // Releases the task without resumption.
+ //
+ // Does nothing if this object does not hold a ticket.
+ ~suspended_task();
+
+ // Returns true if this object holds a ticket for a suspended task.
+ explicit operator bool() const { return resolver_ != nullptr; }
+
+ // Asks the task's executor to resume execution of the suspended task
+ // if it has not already been resumed or completed. Also releases
+ // the task's ticket as a side-effect.
+ //
+ // Clients should call this method when it is possible for the task to
+ // make progress; for example, because some event the task was
+ // awaiting has occurred. See documentation on |fit::executor|.
+ //
+ // Does nothing if this object does not hold a ticket.
+ void resume_task() { resolve(true); }
+
+ // Releases the suspended task without resumption.
+ //
+ // Does nothing if this object does not hold a ticket.
+ void reset() { resolve(false); }
+
+ suspended_task& operator=(const suspended_task& other);
+ suspended_task& operator=(suspended_task&& other);
+
+private:
+ void resolve(bool resume_task);
+
+ resolver* resolver_;
+ ticket ticket_;
+};
+
+} // namespace fit
+
+#endif // LIB_FIT_PROMISE_H_
diff --git a/pkg/fit/include/lib/fit/promise_internal.h b/pkg/fit/include/lib/fit/promise_internal.h
new file mode 100644
index 0000000..30e7085
--- /dev/null
+++ b/pkg/fit/include/lib/fit/promise_internal.h
@@ -0,0 +1,600 @@
+// 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.
+
+#ifndef LIB_FIT_PROMISE_INTERNAL_H_
+#define LIB_FIT_PROMISE_INTERNAL_H_
+
+#include <assert.h>
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "function.h"
+#include "nullable.h"
+#include "result.h"
+#include "traits.h"
+
+namespace fit {
+
+template <typename Continuation>
+class promise_impl;
+
+template <typename Promise>
+class future_impl;
+
+class context;
+class executor;
+class suspended_task;
+
+namespace internal {
+
+// Determines whether a type is a kind of fit::result.
+template <typename Result>
+struct is_result : std::false_type {};
+template <typename V, typename E>
+struct is_result<::fit::result<V, E>> : std::true_type {};
+
+// Deduces a continuation's result.
+// Also ensures that the continuation has a compatible signature.
+template <typename Continuation,
+ typename = std::enable_if_t<is_result<
+ decltype(std::declval<Continuation&>()(
+ std::declval<::fit::context&>()))>::value>>
+struct continuation_traits {
+ using type = Continuation;
+ using result_type = decltype(std::declval<Continuation&>()(std::declval<::fit::context&>()));
+};
+template <typename Continuation, typename = fit::void_t<>>
+struct is_continuation : std::false_type {};
+template <typename Continuation>
+struct is_continuation<
+ Continuation,
+ fit::void_t<typename continuation_traits<Continuation>::type>>
+ : std::true_type {};
+
+// Wraps a handler function and adapts its return type to a fit::result
+// via its specializations.
+template <typename Handler, typename DefaultV, typename DefaultE,
+ typename ReturnType = typename callable_traits<Handler>::return_type,
+ bool callable_result = ::fit::is_callable<ReturnType>::value>
+class result_adapter final {
+ // This expression always evaluates to false but depends on the template
+ // type parameters so that it only gets evaluated when the template is
+ // expanded. If we simply wrote "false", the compiler would raise the
+ // static assertion failure as soon as it encountered the statement.
+ template <typename T>
+ struct check_result { static constexpr bool value = false; };
+ static_assert(
+ check_result<ReturnType>::value,
+ "The provided handler's result type was expected to be "
+ "fit::result<V, E>, fit::ok_result<V>, fit::error_result<E>, "
+ "fit::pending_result, void, or a continuation with the signature "
+ "fit::result<V, E>(fit::context&). "
+ "Please refer to the combinator's documentation for a list of "
+ "supported handler function signatures.");
+};
+
+// Supports handlers that return void.
+template <typename Handler, typename DefaultV, typename DefaultE>
+class result_adapter<Handler, DefaultV, DefaultE, void, false> final {
+public:
+ using result_type = ::fit::result<DefaultV, DefaultE>;
+
+ explicit result_adapter(Handler handler)
+ : handler_(std::move(handler)) {}
+
+ template <typename... Args>
+ result_type call(::fit::context& context, Args... args) {
+ handler_(std::forward<Args>(args)...);
+ return ::fit::ok();
+ }
+
+private:
+ Handler handler_;
+};
+
+// Supports handlers that return pending_result.
+template <typename Handler, typename DefaultV, typename DefaultE>
+class result_adapter<Handler, DefaultV, DefaultE, ::fit::pending_result, false> final {
+public:
+ using result_type = ::fit::result<DefaultV, DefaultE>;
+
+ explicit result_adapter(Handler handler)
+ : handler_(std::move(handler)) {}
+
+ template <typename... Args>
+ result_type call(::fit::context& context, Args... args) {
+ return handler_(std::forward<Args>(args)...);
+ }
+
+private:
+ Handler handler_;
+};
+
+// Supports handlers that return ok_result<V>.
+template <typename Handler, typename DefaultV, typename DefaultE,
+ typename V>
+class result_adapter<Handler, DefaultV, DefaultE, ::fit::ok_result<V>, false> final {
+public:
+ using result_type = ::fit::result<V, DefaultE>;
+
+ explicit result_adapter(Handler handler)
+ : handler_(std::move(handler)) {}
+
+ template <typename... Args>
+ result_type call(::fit::context& context, Args... args) {
+ return handler_(std::forward<Args>(args)...);
+ }
+
+private:
+ Handler handler_;
+};
+
+// Supports handlers that return error_result<E>.
+template <typename Handler, typename DefaultV, typename DefaultE,
+ typename E>
+class result_adapter<Handler, DefaultV, DefaultE, ::fit::error_result<E>, false> final {
+public:
+ using result_type = ::fit::result<DefaultV, E>;
+
+ explicit result_adapter(Handler handler)
+ : handler_(std::move(handler)) {}
+
+ template <typename... Args>
+ result_type call(::fit::context& context, Args... args) {
+ return handler_(std::forward<Args>(args)...);
+ }
+
+private:
+ Handler handler_;
+};
+
+// Supports handlers that return result<V, E>.
+template <typename Handler, typename DefaultV, typename DefaultE,
+ typename V, typename E>
+class result_adapter<Handler, DefaultV, DefaultE, ::fit::result<V, E>, false> final {
+public:
+ using result_type = ::fit::result<V, E>;
+
+ explicit result_adapter(Handler handler)
+ : handler_(std::move(handler)) {}
+
+ template <typename... Args>
+ result_type call(::fit::context& context, Args... args) {
+ return handler_(std::forward<Args>(args)...);
+ }
+
+private:
+ Handler handler_;
+};
+
+// Supports handlers that return continuations or promises.
+// This works for any callable whose signature is:
+// fit::result<...>(fit::context&)
+template <typename Handler, typename DefaultV, typename DefaultE,
+ typename ReturnType>
+class result_adapter<Handler, DefaultV, DefaultE, ReturnType, true> final {
+ // If the handler doesn't actually return a continuation then the
+ // compilation will fail here which is slightly easier to diagnose
+ // than if we dropped the result_adapter specialization entirely.
+ using continuation_traits = continuation_traits<ReturnType>;
+ using continuation_type = typename continuation_traits::type;
+
+public:
+ using result_type = typename continuation_traits::result_type;
+
+ explicit result_adapter(Handler handler)
+ : handler_(std::move(handler)) {}
+
+ template <typename... Args>
+ result_type call(::fit::context& context, Args... args) {
+ if (handler_) {
+ continuation_ = (*handler_)(std::forward<Args>(args)...);
+ handler_.reset();
+ }
+ if (!continuation_) {
+ return ::fit::pending();
+ }
+ return (*continuation_)(context);
+ }
+
+private:
+ ::fit::nullable<Handler> handler_;
+ ::fit::nullable<continuation_type> continuation_;
+};
+
+// Wraps a handler that may or may not have a fit::context& as first argument.
+// This is determined by checking the argument count.
+template <typename Handler, typename DefaultV, typename DefaultE,
+ size_t num_args = 0,
+ int excess_args =
+ (static_cast<int>(
+ ::fit::callable_traits<Handler>::args::size) -
+ static_cast<int>(num_args))>
+class context_adapter final {
+ static_assert(
+ excess_args >= 0,
+ "The provided handler has too few arguments. "
+ "Please refer to the combinator's documentation for a list of "
+ "supported handler function signatures.");
+ static_assert(
+ excess_args <= 1,
+ "The provided handler has too many arguments. "
+ "Please refer to the combinator's documentation for a list of "
+ "supported handler function signatures.");
+};
+
+// Supports handlers without a context argument.
+template <typename Handler, typename DefaultV, typename DefaultE,
+ size_t num_args>
+class context_adapter<Handler, DefaultV, DefaultE, num_args, 0> final {
+ using base_type = result_adapter<Handler, DefaultV, DefaultE>;
+
+public:
+ using result_type = typename base_type::result_type;
+ static constexpr size_t next_arg_index = 0;
+
+ explicit context_adapter(Handler handler)
+ : base_(std::move(handler)) {}
+
+ template <typename... Args>
+ result_type call(::fit::context& context, Args... args) {
+ return base_.template call<Args...>(context, std::forward<Args>(args)...);
+ }
+
+private:
+ base_type base_;
+};
+
+// Supports handlers with a context argument.
+template <typename Handler, typename DefaultV, typename DefaultE,
+ size_t num_args>
+class context_adapter<Handler, DefaultV, DefaultE, num_args, 1> final {
+ using base_type = result_adapter<Handler, DefaultV, DefaultE>;
+ using context_arg_type =
+ typename ::fit::callable_traits<Handler>::args::template at<0>;
+ static_assert(
+ std::is_same<context_arg_type, ::fit::context&>::value,
+ "The provided handler's first argument was expected to be of type "
+ "fit::context& based on the number of arguments it has. "
+ "Please refer to the combinator's documentation for a list of "
+ "supported handler function signatures.");
+
+public:
+ using result_type = typename base_type::result_type;
+ static constexpr size_t next_arg_index = 1;
+
+ explicit context_adapter(Handler handler)
+ : base_(std::move(handler)) {}
+
+ template <typename... Args>
+ result_type call(::fit::context& context, Args... args) {
+ return base_.template call<::fit::context&, Args...>(
+ context, context, std::forward<Args>(args)...);
+ }
+
+private:
+ base_type base_;
+};
+
+// Wraps a handler that may accept a context argument.
+template <typename Handler>
+class context_handler_invoker final {
+ using base_type = context_adapter<Handler, void, void, 0>;
+
+public:
+ using result_type = typename base_type::result_type;
+
+ explicit context_handler_invoker(Handler handler)
+ : base_(std::move(handler)) {}
+
+ result_type operator()(::fit::context& context) {
+ return base_.template call<>(context);
+ }
+
+private:
+ base_type base_;
+};
+
+// Wraps a handler that may accept a context and result argument.
+template <typename Handler, typename PriorResult>
+class result_handler_invoker final {
+ using base_type = context_adapter<Handler, void, void, 1>;
+ using result_arg_type =
+ typename ::fit::callable_traits<Handler>::args::template at<
+ base_type::next_arg_index>;
+ static_assert(
+ (std::is_same<result_arg_type, PriorResult>::value &&
+ std::is_copy_constructible<result_arg_type>::value) ||
+ std::is_same<result_arg_type, PriorResult&>::value ||
+ std::is_same<result_arg_type, const PriorResult&>::value,
+ "The provided handler's last argument was expected to be of type "
+ "fit::result<V, E>&, const fit::result<V, E>&, or fit::result<V, E> "
+ "(if the result is copy-constructible). "
+ "Please refer to the combinator's documentation for a list of "
+ "supported handler function signatures.");
+
+public:
+ using result_type = typename base_type::result_type;
+
+ explicit result_handler_invoker(Handler handler)
+ : base_(std::move(handler)) {}
+
+ result_type operator()(::fit::context& context, PriorResult& result) {
+ return base_.template call<PriorResult&>(context, result);
+ }
+
+private:
+ base_type base_;
+};
+
+// Wraps a handler that may accept a context and value argument.
+template <typename Handler, typename PriorResult,
+ typename V = typename PriorResult::value_type>
+class value_handler_invoker final {
+ using base_type = context_adapter<Handler,
+ void, typename PriorResult::error_type, 1>;
+ using value_arg_type =
+ typename ::fit::callable_traits<Handler>::args::template at<
+ base_type::next_arg_index>;
+ static_assert(
+ (std::is_same<value_arg_type, V>::value &&
+ std::is_copy_constructible<value_arg_type>::value) ||
+ std::is_same<value_arg_type, V&>::value ||
+ std::is_same<value_arg_type, const V&>::value,
+ "The provided handler's last argument was expected to be of type "
+ "V&, const V&, or V (if the value is copy-constructible). "
+ "Please refer to the combinator's documentation for a list of "
+ "supported handler function signatures.");
+
+public:
+ using result_type = typename base_type::result_type;
+
+ explicit value_handler_invoker(Handler handler)
+ : base_(std::move(handler)) {}
+
+ result_type operator()(::fit::context& context, PriorResult& result) {
+ return base_.template call<V&>(context, result.value());
+ }
+
+private:
+ base_type base_;
+};
+
+// Specialization for void value.
+template <typename Handler, typename PriorResult>
+class value_handler_invoker<Handler, PriorResult, void> final {
+ using base_type = context_adapter<Handler,
+ void, typename PriorResult::error_type, 0>;
+
+public:
+ using result_type = typename base_type::result_type;
+
+ explicit value_handler_invoker(Handler handler)
+ : base_(std::move(handler)) {}
+
+ result_type operator()(::fit::context& context, PriorResult& result) {
+ return base_.template call<>(context);
+ }
+
+private:
+ base_type base_;
+};
+
+// Wraps a handler that may accept a context and error argument.
+template <typename Handler, typename PriorResult,
+ typename E = typename PriorResult::error_type>
+class error_handler_invoker final {
+ using base_type = context_adapter<Handler,
+ typename PriorResult::value_type, void, 1>;
+ using error_arg_type =
+ typename ::fit::callable_traits<Handler>::args::template at<
+ base_type::next_arg_index>;
+ static_assert(
+ (std::is_same<error_arg_type, E>::value &&
+ std::is_copy_constructible<error_arg_type>::value) ||
+ std::is_same<error_arg_type, E&>::value ||
+ std::is_same<error_arg_type, const E&>::value,
+ "The provided handler's last argument was expected to be of type "
+ "E&, const E&, or E (if the error is copy-constructible). "
+ "Please refer to the combinator's documentation for a list of "
+ "supported handler function signatures.");
+
+public:
+ using result_type = typename base_type::result_type;
+
+ explicit error_handler_invoker(Handler handler)
+ : base_(std::move(handler)) {}
+
+ result_type operator()(::fit::context& context, PriorResult& result) {
+ return base_.template call<E&>(context, result.error());
+ }
+
+private:
+ base_type base_;
+};
+
+// Specialization for void error.
+template <typename Handler, typename PriorResult>
+class error_handler_invoker<Handler, PriorResult, void> final {
+ using base_type = context_adapter<Handler,
+ typename PriorResult::value_type, void, 0>;
+
+public:
+ using result_type = typename base_type::result_type;
+
+ explicit error_handler_invoker(Handler handler)
+ : base_(std::move(handler)) {}
+
+ result_type operator()(::fit::context& context, PriorResult& result) {
+ return base_.template call<>(context);
+ }
+
+private:
+ base_type base_;
+};
+
+// The continuation produced by |fit::promise::then()|.
+template <typename PriorPromise, typename ResultHandler>
+class then_continuation final {
+ using invoker_type = ::fit::internal::result_handler_invoker<
+ ResultHandler, typename PriorPromise::result_type>;
+
+public:
+ then_continuation(PriorPromise prior_promise, ResultHandler handler)
+ : prior_(std::move(prior_promise)),
+ invoker_(std::move(handler)) {}
+
+ typename invoker_type::result_type operator()(::fit::context& context) {
+ if (!prior_(context))
+ return ::fit::pending();
+ return invoker_(context, prior_.result());
+ }
+
+private:
+ future_impl<PriorPromise> prior_;
+ invoker_type invoker_;
+};
+
+// The continuation produced by |fit::promise::and_then()|.
+template <typename PriorPromise, typename ValueHandler>
+class and_then_continuation final {
+ using invoker_type = ::fit::internal::value_handler_invoker<
+ ValueHandler, typename PriorPromise::result_type>;
+
+public:
+ and_then_continuation(PriorPromise prior_promise, ValueHandler handler)
+ : prior_(std::move(prior_promise)),
+ invoker_(std::move(handler)) {}
+
+ typename invoker_type::result_type operator()(::fit::context& context) {
+ if (!prior_(context))
+ return ::fit::pending();
+ if (prior_.is_error())
+ return prior_.take_error_result();
+ return invoker_(context, prior_.result());
+ }
+
+private:
+ future_impl<PriorPromise> prior_;
+ invoker_type invoker_;
+};
+
+// The continuation produced by |fit::promise::or_else()|.
+template <typename PriorPromise, typename ErrorHandler>
+class or_else_continuation final {
+ using invoker_type = ::fit::internal::error_handler_invoker<
+ ErrorHandler, typename PriorPromise::result_type>;
+
+public:
+ or_else_continuation(PriorPromise prior_promise, ErrorHandler handler)
+ : prior_(std::move(prior_promise)),
+ invoker_(std::move(handler)) {}
+
+ typename invoker_type::result_type operator()(::fit::context& context) {
+ if (!prior_(context))
+ return ::fit::pending();
+ if (prior_.is_ok())
+ return prior_.take_ok_result();
+ return invoker_(context, prior_.result());
+ }
+
+private:
+ future_impl<PriorPromise> prior_;
+ invoker_type invoker_;
+};
+
+// The continuation produced by |fit::promise::inspect()|.
+template <typename PriorPromise, typename InspectHandler>
+class inspect_continuation final {
+ using invoker_type = ::fit::internal::result_handler_invoker<
+ InspectHandler, typename PriorPromise::result_type>;
+
+public:
+ inspect_continuation(PriorPromise prior_promise, InspectHandler handler)
+ : prior_(std::move(prior_promise)),
+ invoker_(std::move(handler)) {}
+
+ typename PriorPromise::result_type operator()(::fit::context& context) {
+ typename PriorPromise::result_type result = prior_(context);
+ if (result)
+ invoker_(context, result);
+ return result;
+ }
+
+private:
+ PriorPromise prior_;
+ invoker_type invoker_;
+};
+
+// The continuation produced by |fit::promise::discard_result()|.
+template <typename PriorPromise>
+class discard_result_continuation final {
+public:
+ explicit discard_result_continuation(PriorPromise prior_promise)
+ : prior_(std::move(prior_promise)) {}
+
+ fit::result<> operator()(::fit::context& context) {
+ if (!prior_(context))
+ return ::fit::pending();
+ return ::fit::ok();
+ }
+
+private:
+ PriorPromise prior_;
+};
+
+// The continuation produced by |make_promise()|.
+// This turns out to be equivalent to a context handler invoker.
+template <typename PromiseHandler>
+using promise_continuation = context_handler_invoker<PromiseHandler>;
+
+// Returns true if all arguments are true or if there are none.
+inline bool all_true() {
+ return true;
+}
+template <typename... Ts>
+inline bool all_true(bool value, Ts... values) {
+ return value & all_true(values...);
+}
+
+// The continuation produced by |join_promises()|.
+template <typename... Promises>
+class join_continuation final {
+public:
+ explicit join_continuation(Promises... promises)
+ : promises_(std::make_tuple(std::move(promises)...)) {}
+
+ ::fit::result<std::tuple<typename Promises::result_type...>> operator()(
+ ::fit::context& context) {
+ return evaluate(context, std::index_sequence_for<Promises...>{});
+ }
+
+private:
+ template <size_t... i>
+ ::fit::result<std::tuple<typename Promises::result_type...>> evaluate(
+ ::fit::context& context, std::index_sequence<i...>) {
+ bool done = all_true(std::get<i>(promises_)(context)...);
+ if (!done)
+ return ::fit::pending();
+ return ::fit::ok(std::make_tuple(std::get<i>(promises_).take_result()...));
+ }
+
+ std::tuple<future_impl<Promises>...> promises_;
+};
+
+} // namespace internal
+
+template <typename PromiseHandler>
+inline promise_impl<::fit::internal::promise_continuation<PromiseHandler>>
+make_promise(PromiseHandler handler);
+
+template <typename Continuation>
+inline promise_impl<Continuation> make_promise_with_continuation(
+ Continuation continuation);
+
+} // namespace fit
+
+#endif // LIB_FIT_PROMISE_INTERNAL_H_
diff --git a/pkg/fit/include/lib/fit/result.h b/pkg/fit/include/lib/fit/result.h
new file mode 100644
index 0000000..eb89baf
--- /dev/null
+++ b/pkg/fit/include/lib/fit/result.h
@@ -0,0 +1,270 @@
+// 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.
+
+#ifndef LIB_FIT_RESULT_H_
+#define LIB_FIT_RESULT_H_
+
+#include <assert.h>
+
+#include <new>
+#include <type_traits>
+#include <utility>
+
+#include "traits.h"
+#include "variant.h"
+
+namespace fit {
+
+// Represents the intermediate state of a result that has not yet completed.
+struct pending_result final {};
+
+// Returns an value that represents a pending result.
+constexpr inline pending_result pending() {
+ return pending_result{};
+}
+
+// Represents the result of a successful task.
+template <typename V = void>
+struct ok_result final {
+ using value_type = V;
+
+ explicit constexpr ok_result(V value)
+ : value(std::move(value)) {}
+
+ V value;
+};
+template <>
+struct ok_result<void> {
+ using value_type = void;
+};
+
+// Wraps the result of a successful task as an |ok_result<T>|.
+template <typename V>
+constexpr inline ok_result<V> ok(V value) {
+ return ok_result<V>(std::move(value));
+}
+constexpr inline ok_result<> ok() {
+ return ok_result<>{};
+}
+
+// Represents the result of a failed task.
+template <typename E = void>
+struct error_result final {
+ using error_type = E;
+
+ explicit constexpr error_result(E error)
+ : error(std::move(error)) {}
+
+ E error;
+};
+template <>
+struct error_result<void> {
+ using error_type = void;
+};
+
+// Wraps the result of a failed task as an |error_result<T>|.
+template <typename E>
+constexpr inline error_result<E> error(E error) {
+ return error_result<E>(std::move(error));
+}
+constexpr inline error_result<> error() {
+ return error_result<>{};
+}
+
+// Describes the status of a task's result.
+enum class result_state {
+ // The task is still in progress.
+ pending,
+ // The task completed successfully.
+ ok,
+ // The task failed.
+ error
+};
+
+// Represents the result of a task which may have succeeded, failed,
+// or still be in progress.
+//
+// Use |fit::pending()|, |fit::ok<T>()|, or |fit::error<T>| to initialize
+// the result.
+//
+// |V| is the type of value produced when the completes successfully.
+// Defaults to |void|.
+//
+// |E| is the type of error produced when the completes with an error.
+// Defaults to |void|.
+//
+// EXAMPLE:
+//
+// fit::result<int, std::string> divide(int dividend, int divisor) {
+// if (divisor == 0)
+// return fit::error<std::string>("divide by zero");
+// return fit::ok(dividend / divisor);
+// }
+//
+// int try_divide(int dividend, int divisor) {
+// auto result = divide(dividend, divisor);
+// if (result.is_ok()) {
+// printf("%d / %d = %d\n", dividend, divisor, result.value());
+// return result.value();
+// }
+// printf("%d / %d: ERROR %s\n", dividend, divisor, result.error().c_str());
+// return -999;
+// }
+//
+// EXAMPLE WITH VOID RESULT VALUE AND ERROR:
+//
+// fit::result<> open(std::string secret) {
+// printf("guessing \"%s\"\n", secret.c_str());
+// if (secret == "sesame") {
+// return fit::ok();
+// puts("yes!");
+// }
+// puts("no.");
+// return fit::error();
+// }
+//
+// bool guess_combination() {
+// return open("friend") || open("sesame") || open("I give up");
+// }
+template <typename V = void, typename E = void>
+class result final {
+public:
+ using value_type = V;
+ using error_type = E;
+
+ // Creates a pending result.
+ constexpr result() = default;
+ constexpr result(pending_result) {}
+
+ // Creates an ok result.
+ constexpr result(ok_result<V> result)
+ : state_(::fit::internal::in_place_index<1>, std::move(result)) {}
+ template <typename OtherV,
+ typename = std::enable_if_t<std::is_constructible<V, OtherV>::value>>
+ constexpr result(ok_result<OtherV> other)
+ : state_(::fit::internal::in_place_index<1>,
+ fit::ok<V>(std::move(other.value))) {}
+
+ // Creates an error result.
+ constexpr result(error_result<E> result)
+ : state_(::fit::internal::in_place_index<2>, std::move(result)) {}
+ template <typename OtherE,
+ typename = std::enable_if_t<std::is_constructible<E, OtherE>::value>>
+ constexpr result(error_result<OtherE> other)
+ : state_(::fit::internal::in_place_index<2>,
+ fit::error<E>(std::move(other.error))) {}
+
+ // Copies another result (if copyable).
+ result(const result& other) = default;
+
+ // Moves from another result, leaving the other one in a pending state.
+ result(result&& other)
+ : state_(std::move(other.state_)) {
+ other.reset();
+ }
+
+ ~result() = default;
+
+ // Returns the state of the task's result: pending, ok, or error.
+ constexpr result_state state() const {
+ return static_cast<result_state>(state_.index());
+ }
+
+ // Returns true if the result is not pending.
+ constexpr explicit operator bool() const {
+ return !is_pending();
+ }
+
+ // Returns true if the task is still in progress.
+ constexpr bool is_pending() const {
+ return state() == result_state::pending;
+ }
+
+ // Returns true if the task succeeded.
+ constexpr bool is_ok() const {
+ return state() == result_state::ok;
+ }
+
+ // Returns true if the task failed.
+ constexpr bool is_error() const {
+ return state() == result_state::error;
+ }
+
+ // Gets the result's value.
+ // Asserts that the result's state is |fit::result_state::ok|.
+ template <typename R = V,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ constexpr R& value() {
+ return state_.template get<1>().value;
+ }
+ template <typename R = V,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ constexpr const R& value() const {
+ return state_.template get<1>().value;
+ }
+
+ // Takes the result's value, leaving it in a pending state.
+ // Asserts that the result's state is |fit::result_state::ok|.
+ template <typename R = V,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ R take_value() {
+ auto value = std::move(state_.template get<1>().value);
+ reset();
+ return value;
+ }
+ ok_result<V> take_ok_result() {
+ auto result = std::move(state_.template get<1>());
+ reset();
+ return result;
+ }
+
+ // Gets a reference to the result's error.
+ // Asserts that the result's state is |fit::result_state::error|.
+ template <typename R = E,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ constexpr R& error() {
+ return state_.template get<2>().error;
+ }
+ template <typename R = E,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ constexpr const R& error() const {
+ return state_.template get<2>().error;
+ }
+
+ // Takes the result's error, leaving it in a pending state.
+ // Asserts that the result's state is |fit::result_state::error|.
+ template <typename R = E,
+ typename = std::enable_if_t<!std::is_void<R>::value>>
+ R take_error() {
+ auto error = std::move(state_.template get<2>().error);
+ reset();
+ return error;
+ }
+ error_result<E> take_error_result() {
+ auto result = std::move(state_.template get<2>());
+ reset();
+ return result;
+ }
+
+ // Assigns from another result (if copyable).
+ result& operator=(const result& other) = default;
+
+ // Moves from another result, leaving the other one in a pending state.
+ result& operator=(result&& other) {
+ state_ = std::move(other.state_);
+ other.reset();
+ return *this;
+ }
+
+private:
+ void reset() { state_.template emplace<0>(); }
+
+ ::fit::internal::variant<
+ ::fit::internal::monostate, ok_result<V>, error_result<E>>
+ state_;
+};
+
+} // namespace fit
+
+#endif // LIB_FIT_RESULT_H_
diff --git a/pkg/fit/include/lib/fit/scheduler.h b/pkg/fit/include/lib/fit/scheduler.h
new file mode 100644
index 0000000..d4e16ba
--- /dev/null
+++ b/pkg/fit/include/lib/fit/scheduler.h
@@ -0,0 +1,154 @@
+// 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.
+
+#ifndef LIB_FIT_SCHEDULER_H_
+#define LIB_FIT_SCHEDULER_H_
+
+#include <map>
+#include <queue>
+#include <utility>
+
+#include "promise.h"
+
+namespace fit {
+namespace subtle {
+
+// Keeps track of runnable and suspended tasks.
+// This is a low-level building block for implementing executors.
+// For a concrete implementation, see |fit::single_threaded_executor|.
+//
+// Instances of this object are not thread-safe. Its client is responsible
+// for providing all necessary synchronization.
+class scheduler final {
+public:
+ using task_queue = std::queue<pending_task>;
+ using ref_count = uint32_t;
+
+ scheduler();
+ ~scheduler();
+
+ // Adds a task to the runnable queue.
+ //
+ // Preconditions:
+ // - |task| must be non-empty
+ void schedule_task(pending_task task);
+
+ // Obtains a new ticket with a ref-count of |initial_refs|.
+ // The executor must eventually call |finalize_ticket()| to update the
+ // state of the ticket.
+ //
+ // Preconditions:
+ // - |initial_refs| must be at least 1
+ suspended_task::ticket obtain_ticket(ref_count initial_refs = 1);
+
+ // Updates a ticket after one run of a task's continuation according
+ // to the state of the task after its run. The executor must call this
+ // method after calling |obtain_ticket()| to indicate the disposition of
+ // the task for which the ticket was obtained.
+ //
+ // Passing an empty |task| indicates that the task has completed so it
+ // does not need to be resumed.
+ //
+ // Passing a non-empty |task| indicates that the task returned a pending
+ // result and may need to be suspended depending on the current state
+ // of the ticket.
+ // - If the ticket has already been resumed, moves |task| into the
+ // runnable queue.
+ // - Otherwise, if the ticket still has a non-zero ref-count, moves |task|
+ // into the suspended task table.
+ // - Otherwise, considers the task abandoned and the caller retains
+ // ownership of |task|.
+ //
+ // Preconditions:
+ // - |task| must be non-null (may be empty)
+ // - the ticket must not have already been finalized
+ void finalize_ticket(suspended_task::ticket ticket, pending_task* task);
+
+ // Increments the ticket's ref-count.
+ //
+ // Preconditions:
+ // - the ticket's ref-count must be non-zero (positive)
+ void duplicate_ticket(suspended_task::ticket ticket);
+
+ // Decrements the ticket's ref-count.
+ //
+ // If the task's ref-count reaches 0 and has an associated task that
+ // has not already been resumed, returns the associated task back
+ // to the caller.
+ // Otherwise, returns an empty task.
+ //
+ // Preconditions:
+ // - the ticket's ref-count must be non-zero (positive)
+ pending_task release_ticket(suspended_task::ticket ticket);
+
+ // Resumes a task and decrements the ticket's ref-count.
+ //
+ // If the ticket has an associated task that has not already been resumed,
+ // moves its associated task to the runnable queue and returns true.
+ // Otherwise, returns false.
+ //
+ // Preconditions:
+ // - the ticket's ref-count must be non-zero (positive)
+ bool resume_task_with_ticket(suspended_task::ticket ticket);
+
+ // Takes all tasks in the runnable queue.
+ //
+ // Preconditions:
+ // - |tasks| must be non-null and empty
+ void take_runnable_tasks(task_queue* tasks);
+
+ // Takes all remaining tasks, regardless of whether they are runnable
+ // or suspended.
+ //
+ // This operation is useful when shutting down an executor.
+ //
+ // Preconditions:
+ // - |tasks| must be non-null and empty
+ void take_all_tasks(task_queue* tasks);
+
+ // Returns true if there are any runnable tasks.
+ bool has_runnable_tasks() const { return !runnable_tasks_.empty(); }
+
+ // Returns true if there are any suspended tasks that have yet to
+ // be resumed.
+ bool has_suspended_tasks() const { return suspended_task_count_ > 0; }
+
+ // Returns true if there are any tickets that have yet to be finalized,
+ // released, or resumed.
+ bool has_outstanding_tickets() const { return !tickets_.empty(); }
+
+ scheduler(const scheduler&) = delete;
+ scheduler(scheduler&&) = delete;
+ scheduler& operator=(const scheduler&) = delete;
+ scheduler& operator=(scheduler&&) = delete;
+
+private:
+ struct ticket_record {
+ ticket_record(ref_count initial_refs)
+ : ref_count(initial_refs), was_resumed(false) {}
+
+ // The current reference count.
+ ref_count ref_count;
+
+ // True if the task has been resumed using |resume_task_with_ticket()|.
+ bool was_resumed;
+
+ // The task is initially empty when the ticket is obtained.
+ // It is later set to non-empty if the task needs to be suspended when
+ // the ticket is finalized. It becomes empty again when the task
+ // is moved into the runnable queue, released, or taken.
+ pending_task task;
+ };
+ using ticket_map = std::map<suspended_task::ticket, ticket_record>;
+
+ task_queue runnable_tasks_;
+ ticket_map tickets_;
+ uint64_t suspended_task_count_ = 0;
+ suspended_task::ticket next_ticket_ = 1;
+};
+
+} // namespace subtle
+} // namespace fit
+
+#endif // LIB_FIT_SCHEDULER_H_
diff --git a/pkg/fit/include/lib/fit/sequencer.h b/pkg/fit/include/lib/fit/sequencer.h
new file mode 100644
index 0000000..509cd8e
--- /dev/null
+++ b/pkg/fit/include/lib/fit/sequencer.h
@@ -0,0 +1,85 @@
+// 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.
+
+#ifndef LIB_FIT_SEQUENCER_H_
+#define LIB_FIT_SEQUENCER_H_
+
+#include <assert.h>
+
+#include <mutex>
+
+#include "bridge.h"
+#include "thread_safety.h"
+
+namespace fit {
+
+// A sequencer imposes a first-in-first-out sequential execution order onto a
+// sequence of promises. Each successively enqueued promise remains suspended
+// until all previously enqueued promises complete or are abandoned.
+//
+// |fit::sequencer| is designed to be used either on its own or chained
+// onto a promise using |fit::promise::wrap_with()|.
+//
+// EXAMPLE
+//
+// // This wrapper type is intended to be applied to
+// // a sequence of promises so we store it in a variable.
+// fit::sequencer seq;
+//
+// // This task consists of some amount of work that must be
+// // completed sequentially followed by other work that can
+// // happen in any order. We use |wrap_with()| to wrap the
+// // sequential work with the sequencer.
+// fit::promise<> perform_complex_task() {
+// return fit::make_promise([] { /* do sequential work */ })
+// .then([] (fit::result<> result) { /* this will also be wrapped */ })
+// .wrap_with(seq)
+// .then([] (fit::result<> result) { /* do more work */ });
+// }
+//
+class sequencer final {
+public:
+ sequencer();
+ ~sequencer();
+
+ // Returns a new promise which will invoke |promise| after all previously
+ // enqueued promises on this sequencer have completed or been abandoned.
+ //
+ // This method is thread-safe.
+ template <typename Promise>
+ decltype(auto) wrap(Promise promise) {
+ assert(promise);
+
+ fit::bridge<> bridge;
+ fit::consumer<> prior = swap_prior(std::move(bridge.consumer()));
+ return prior.promise_or(fit::ok())
+ .then([promise = std::move(promise),
+ completer = std::move(bridge.completer())](
+ fit::context& context, fit::result<>) mutable {
+ // This handler will run once the completer associated
+ // with the |prior| promise is abandoned. Once the promise
+ // has finished, both the promise and completer will be
+ // destroyed thereby causing the next promise chained onto
+ // the |bridge|'s associated consumer to become runnable.
+ return promise(context);
+ });
+ }
+
+ sequencer(const sequencer&) = delete;
+ sequencer(sequencer&&) = delete;
+ sequencer& operator=(const sequencer&) = delete;
+ sequencer& operator=(sequencer&&) = delete;
+
+private:
+ fit::consumer<> swap_prior(fit::consumer<> new_prior);
+
+ std::mutex mutex_;
+
+ // Holds the consumption capability of the most recently wrapped promise.
+ fit::consumer<> prior_ FIT_GUARDED(mutex_);
+};
+
+} // namespace fit
+
+#endif // LIB_FIT_SEQUENCER_H_
diff --git a/pkg/fit/include/lib/fit/single_threaded_executor.h b/pkg/fit/include/lib/fit/single_threaded_executor.h
new file mode 100644
index 0000000..7ec4875
--- /dev/null
+++ b/pkg/fit/include/lib/fit/single_threaded_executor.h
@@ -0,0 +1,85 @@
+// 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.
+
+#ifndef LIB_FIT_SINGLE_THREADED_EXECUTOR_H_
+#define LIB_FIT_SINGLE_THREADED_EXECUTOR_H_
+
+#include <utility>
+
+#include "promise.h"
+#include "scheduler.h"
+
+namespace fit {
+
+// A simple platform-independent single-threaded asynchronous task executor.
+//
+// This implementation is designed for use when writing simple single-threaded
+// platform-independent applications. It may be less efficient or provide
+// fewer features than more specialized or platform-dependent executors.
+//
+// See documentation of |fit::promise| for more information.
+class single_threaded_executor final : public executor {
+public:
+ single_threaded_executor();
+
+ // Destroys the executor along with all of its remaining scheduled tasks
+ // that have yet to complete.
+ ~single_threaded_executor() override;
+
+ // Schedules a task for eventual execution by the executor.
+ //
+ // This method is thread-safe.
+ void schedule_task(pending_task task) override;
+
+ // Runs all scheduled tasks (including additional tasks scheduled while
+ // they run) until none remain.
+ //
+ // This method is thread-safe but must only be called on at most one
+ // thread at a time.
+ void run();
+
+ single_threaded_executor(const single_threaded_executor&) = delete;
+ single_threaded_executor(single_threaded_executor&&) = delete;
+ single_threaded_executor& operator=(const single_threaded_executor&) = delete;
+ single_threaded_executor& operator=(single_threaded_executor&&) = delete;
+
+private:
+ class dispatcher_impl;
+
+ // The task context for tasks run by the executor.
+ class context_impl final : public context {
+ public:
+ explicit context_impl(single_threaded_executor* executor);
+ ~context_impl() override;
+
+ single_threaded_executor* executor() const override;
+ suspended_task suspend_task() override;
+
+ private:
+ single_threaded_executor* const executor_;
+ };
+
+ context_impl context_;
+ dispatcher_impl* const dispatcher_;
+};
+
+// Creates a new |fit::single_threaded_executor|, schedules a promise as a task,
+// runs all of the executor's scheduled tasks until none remain, then returns
+// the promise's result.
+template <typename Continuation>
+static typename promise_impl<Continuation>::result_type
+run_single_threaded(promise_impl<Continuation> promise) {
+ using result_type = typename promise_impl<Continuation>::result_type;
+ single_threaded_executor exec;
+ result_type saved_result;
+ exec.schedule_task(promise.then([&saved_result](result_type result) {
+ saved_result = std::move(result);
+ }));
+ exec.run();
+ return saved_result;
+}
+
+} // namespace fit
+
+#endif // LIB_FIT_SINGLE_THREADED_EXECUTOR_H_
diff --git a/pkg/fit/include/lib/fit/thread_safety.h b/pkg/fit/include/lib/fit/thread_safety.h
new file mode 100644
index 0000000..8ffc5ad
--- /dev/null
+++ b/pkg/fit/include/lib/fit/thread_safety.h
@@ -0,0 +1,30 @@
+// 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.
+
+#ifndef LIB_FIT_THREAD_SAFETY_H_
+#define LIB_FIT_THREAD_SAFETY_H_
+
+// Thread-safety annotations.
+// Currently these are only supported on Clang.
+#if defined(__clang__) && \
+ defined(_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS) && \
+ __has_attribute(acquire_capability)
+#define FIT_THREAD_ANNOTATION(x) __attribute__((x))
+#else
+#define FIT_THREAD_ANNOTATION(x)
+#endif
+#define FIT_CAPABILITY(x) FIT_THREAD_ANNOTATION(__capability__(x))
+#define FIT_GUARDED(x) FIT_THREAD_ANNOTATION(__guarded_by__(x))
+#define FIT_ACQUIRE(...) FIT_THREAD_ANNOTATION(__acquire_capability__(__VA_ARGS__))
+#define FIT_TRY_ACQUIRE(...) FIT_THREAD_ANNOTATION(__try_acquire_capability__(__VA_ARGS__))
+#define FIT_ACQUIRED_BEFORE(...) FIT_THREAD_ANNOTATION(__acquired_before__(__VA_ARGS__))
+#define FIT_ACQUIRED_AFTER(...) FIT_THREAD_ANNOTATION(__acquired_after__(__VA_ARGS__))
+#define FIT_RELEASE(...) FIT_THREAD_ANNOTATION(__release_capability__(__VA_ARGS__))
+#define FIT_REQUIRES(...) FIT_THREAD_ANNOTATION(__requires_capability__(__VA_ARGS__))
+#define FIT_EXCLUDES(...) FIT_THREAD_ANNOTATION(__locks_excluded__(__VA_ARGS__))
+#define FIT_RETURN_CAPABILITY(x) FIT_THREAD_ANNOTATION(__lock_returned__(x))
+#define FIT_SCOPED_CAPABILITY FIT_THREAD_ANNOTATION(__scoped_lockable__)
+#define FIT_NO_THREAD_SAFETY_ANALYSIS FIT_THREAD_ANNOTATION(__no_thread_safety_analysis__)
+
+#endif // LIB_FIT_THREAD_SAFETY_H_
diff --git a/pkg/fit/include/lib/fit/traits.h b/pkg/fit/include/lib/fit/traits.h
new file mode 100644
index 0000000..933005e
--- /dev/null
+++ b/pkg/fit/include/lib/fit/traits.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef LIB_FIT_TRAITS_H_
+#define LIB_FIT_TRAITS_H_
+
+#include <tuple>
+#include <type_traits>
+
+namespace fit {
+
+// C++ 14 compatible implementation of std::void_t.
+#if defined(__cplusplus) && __cplusplus >= 201703L
+template <typename... T>
+using void_t = std::void_t<T...>;
+#else
+template <typename... T>
+struct make_void { typedef void type; };
+template <typename... T>
+using void_t = typename make_void<T...>::type;
+#endif
+
+// Encapsulates capture of a parameter pack. Typical use is to use instances of this empty struct
+// for type dispatch in function template deduction/overload resolution.
+//
+// Example:
+// template <typename Callable, typename... Args>
+// auto inspect_args(Callable c, parameter_pack<Args...>) {
+// // do something with Args...
+// }
+//
+// template <typename Callable>
+// auto inspect_args(Callable c) {
+// return inspect_args(std::move(c), typename callable_traits<Callable>::args{});
+// }
+template <typename... T>
+struct parameter_pack {
+ static constexpr size_t size = sizeof...(T);
+
+ template <size_t i>
+ using at = typename std::tuple_element_t<i, std::tuple<T...>>;
+};
+
+// |callable_traits| captures elements of interest from function-like types (functions, function
+// pointers, and functors, including lambdas). Due to common usage patterns, const and non-const
+// functors are treated identically.
+//
+// Member types:
+// |args| - a |parameter_pack| that captures the parameter types of the function. See
+// |parameter_pack| for usage and details.
+// |return_type| - the return type of the function.
+// |type| - the underlying functor or function pointer type. This member is absent if
+// |callable_traits| are requested for a raw function signature (as opposed to a
+// function pointer or functor; e.g. |callable_traits<void()>|).
+// |signature| - the type of the equivalent function.
+
+template <typename T>
+struct callable_traits : public callable_traits<decltype(&T::operator())> {};
+
+// Treat mutable call operators the same as const call operators.
+//
+// It would be equivalent to erase the const instead, but the common case is lambdas, which are
+// const, so prefer to nest less deeply for the common const case.
+template <typename FunctorType, typename ReturnType, typename... ArgTypes>
+struct callable_traits<ReturnType (FunctorType::*)(ArgTypes...)>
+ : public callable_traits<ReturnType (FunctorType::*)(ArgTypes...) const> {};
+
+// Common functor specialization.
+template <typename FunctorType, typename ReturnType, typename... ArgTypes>
+struct callable_traits<ReturnType (FunctorType::*)(ArgTypes...) const>
+ : public callable_traits<ReturnType (*)(ArgTypes...)> {
+
+ using type = FunctorType;
+};
+
+// Function pointer specialization.
+template <typename ReturnType, typename... ArgTypes>
+struct callable_traits<ReturnType (*)(ArgTypes...)>
+ : public callable_traits<ReturnType(ArgTypes...)> {
+
+ using type = ReturnType (*)(ArgTypes...);
+};
+
+// Base specialization.
+template <typename ReturnType, typename... ArgTypes>
+struct callable_traits<ReturnType(ArgTypes...)> {
+ using signature = ReturnType(ArgTypes...);
+ using return_type = ReturnType;
+ using args = parameter_pack<ArgTypes...>;
+
+ callable_traits() = delete;
+};
+
+// Determines whether a type has an operator() that can be invoked.
+template <typename T, typename = void_t<>>
+struct is_callable : public std::false_type {};
+template <typename ReturnType, typename... ArgTypes>
+struct is_callable<ReturnType (*)(ArgTypes...)>
+ : public std::true_type {};
+template <typename FunctorType, typename ReturnType, typename... ArgTypes>
+struct is_callable<ReturnType (FunctorType::*)(ArgTypes...)>
+ : public std::true_type {};
+template <typename T>
+struct is_callable<T, void_t<decltype(&T::operator())>>
+ : public std::true_type {};
+
+} // namespace fit
+
+#endif // LIB_FIT_TRAITS_H_
diff --git a/pkg/fit/include/lib/fit/variant.h b/pkg/fit/include/lib/fit/variant.h
new file mode 100644
index 0000000..f91a4a2
--- /dev/null
+++ b/pkg/fit/include/lib/fit/variant.h
@@ -0,0 +1,404 @@
+// 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.
+
+#ifndef LIB_FIT_VARIANT_H_
+#define LIB_FIT_VARIANT_H_
+
+#include <assert.h>
+
+#include <new>
+#include <type_traits>
+#include <utility>
+
+namespace fit {
+namespace internal {
+
+// This is a very basic partial implementation of |std::variant| which
+// is compatible with C++ 14. In its current state, it only implements
+// enough of the API for internal usage. Transforming this into a more
+// complete and precise implementation of |std::variant| is left as an
+// exercise for the reader. ;)
+//
+// Some key differences:
+// - always requires first type to be monostate
+// - always default constructible
+// - no relational operators
+// - no visitors
+// - no find by type, only find by index
+// - no exception support
+// - simplified get/set methods
+
+// Unit type.
+struct monostate final {
+ constexpr bool operator==(const monostate& other) const { return true; }
+ constexpr bool operator!=(const monostate& other) const { return false; }
+};
+
+// Tag for requesting in-place initialization of a variant alternative by index.
+template <size_t index>
+struct in_place_index_t final {};
+
+#ifdef __cpp_inline_variables
+
+// Inline variables are only available on C++ 17 and beyond.
+template <size_t index>
+inline constexpr in_place_index_t<index> in_place_index{};
+
+#else
+
+// On C++ 14 we need to provide storage for the variable so we define
+// |in_place_index| as a reference instead.
+template <size_t index>
+struct in_place_index_holder {
+ static constexpr in_place_index_t<index> instance{};
+};
+
+template <size_t index>
+constexpr in_place_index_t<index> in_place_index_holder<index>::instance;
+
+template <size_t index>
+static constexpr const in_place_index_t<index>& in_place_index =
+ in_place_index_holder<index>::instance;
+
+#endif // __cpp_inline_variables
+
+// Stores the contents of the variant as a recursively nested union
+// of alternatives. Conceptually it might be simpler to use
+// std::in_place_storage<> but we would lose the ability to treat the
+// storage as a literal type.
+template <typename... Ts>
+union variant_storage final {
+ static constexpr bool copy_construct_supported = true;
+ static constexpr bool move_construct_supported = true;
+
+ void copy_construct_from(size_t index, const variant_storage& other) {}
+ void move_construct_from(size_t index, variant_storage&& other) {}
+ void destroy(size_t index) {}
+ void swap(size_t index, variant_storage& other) {}
+};
+
+template <typename T0, typename... Ts>
+union variant_storage<T0, Ts...> final {
+ static constexpr bool copy_construct_supported =
+ std::is_copy_constructible<T0>::value &&
+ variant_storage<Ts...>::copy_construct_supported;
+ static constexpr bool move_construct_supported =
+ std::is_move_constructible<T0>::value &&
+ variant_storage<Ts...>::move_construct_supported;
+
+ constexpr variant_storage() = default;
+
+ template <typename... Args>
+ explicit constexpr variant_storage(in_place_index_t<0>, Args&&... args)
+ : alt(std::forward<Args>(args)...) {}
+
+ template <size_t index, typename... Args>
+ explicit constexpr variant_storage(in_place_index_t<index>, Args&&... args)
+ : rest(in_place_index<index - 1>, std::forward<Args>(args)...) {}
+
+ constexpr T0& get(in_place_index_t<0>) { return alt; }
+
+ constexpr const T0& get(in_place_index_t<0>) const { return alt; }
+
+ template <size_t index>
+ constexpr auto& get(in_place_index_t<index>) {
+ return rest.get(in_place_index<index - 1>);
+ }
+
+ template <size_t index>
+ constexpr const auto& get(in_place_index_t<index>) const {
+ return rest.get(in_place_index<index - 1>);
+ }
+
+ template <typename... Args>
+ auto& emplace(in_place_index_t<0>, Args&&... args) {
+ new (&alt) T0(std::forward<Args>(args)...);
+ return alt;
+ }
+
+ template <size_t index, typename... Args>
+ auto& emplace(in_place_index_t<index>, Args&&... args) {
+ return rest.emplace(in_place_index<index - 1>,
+ std::forward<Args>(args)...);
+ }
+
+ void copy_construct_from(size_t index, const variant_storage& other) {
+ if (index == 0) {
+ new (&alt) T0(other.alt);
+ } else {
+ rest.copy_construct_from(index - 1, other.rest);
+ }
+ }
+
+ void move_construct_from(size_t index, variant_storage&& other) {
+ if (index == 0) {
+ new (&alt) T0(std::move(other.alt));
+ } else {
+ rest.move_construct_from(index - 1, std::move(other.rest));
+ }
+ }
+
+ void destroy(size_t index) {
+ if (index == 0) {
+ alt.~T0();
+ } else {
+ rest.destroy(index - 1);
+ }
+ }
+
+ void swap(size_t index, variant_storage& other) {
+ using std::swap;
+ if (index == 0) {
+ swap(alt, other.alt);
+ } else {
+ rest.swap(index - 1, other.rest);
+ }
+ }
+
+ T0 alt;
+ variant_storage<Ts...> rest;
+};
+
+// Holds the index and storage for a variant with a trivial destructor.
+template <typename... Ts>
+class variant_base_impl_trivial;
+template <typename... Ts>
+class variant_base_impl_trivial<monostate, Ts...> {
+public:
+ constexpr variant_base_impl_trivial()
+ : index_(0),
+ storage_(in_place_index<0>, monostate{}) {}
+
+ template <size_t index, typename... Args>
+ explicit constexpr variant_base_impl_trivial(
+ in_place_index_t<index>, Args&&... args)
+ : index_(index),
+ storage_(in_place_index<index>, std::forward<Args>(args)...) {}
+
+ // Used by emplace.
+ void destroy() {}
+
+protected:
+ size_t index_;
+ variant_storage<monostate, Ts...> storage_;
+};
+
+// Holds the index and storage for a variant with a non-trivial destructor.
+template <typename... Ts>
+class variant_base_impl_non_trivial;
+template <typename... Ts>
+class variant_base_impl_non_trivial<monostate, Ts...> {
+public:
+ constexpr variant_base_impl_non_trivial()
+ : index_(0),
+ storage_(in_place_index<0>, monostate{}) {}
+
+ template <size_t index, typename... Args>
+ explicit constexpr variant_base_impl_non_trivial(
+ in_place_index_t<index>, Args&&... args)
+ : index_(index),
+ storage_(in_place_index<index>, std::forward<Args>(args)...) {}
+
+ ~variant_base_impl_non_trivial() {
+ destroy();
+ }
+
+ // Used by emplace and by the destructor.
+ void destroy() {
+ storage_.destroy(index_);
+ }
+
+protected:
+ size_t index_;
+ union {
+ variant_storage<monostate, Ts...> storage_;
+ };
+};
+
+// Selects an appropriate variant base class depending on whether
+// its destructor is trivial or non-trivial.
+template <typename... Ts>
+using variant_base_impl =
+ std::conditional_t<
+ std::is_destructible<
+ variant_base_impl_trivial<Ts...>>::value,
+ variant_base_impl_trivial<Ts...>,
+ variant_base_impl_non_trivial<Ts...>>;
+
+// Implements non-trivial move-construction and move-assignment.
+template <typename... Ts>
+class variant_move_impl_non_trivial : protected variant_base_impl<Ts...> {
+ using base = variant_base_impl<Ts...>;
+
+public:
+ using base::base;
+
+ variant_move_impl_non_trivial(
+ const variant_move_impl_non_trivial& other) = default;
+
+ variant_move_impl_non_trivial(
+ variant_move_impl_non_trivial&& other) {
+ index_ = other.index_;
+ storage_.move_construct_from(index_, std::move(other.storage_));
+ }
+
+ variant_move_impl_non_trivial& operator=(
+ const variant_move_impl_non_trivial& other) = default;
+
+ variant_move_impl_non_trivial& operator=(
+ variant_move_impl_non_trivial&& other) {
+ if (&other == this)
+ return *this;
+ storage_.destroy(index_);
+ index_ = other.index_;
+ storage_.move_construct_from(index_, std::move(other.storage_));
+ return *this;
+ }
+
+protected:
+ using base::index_;
+ using base::storage_;
+};
+
+// Selects an appropriate variant base class for moving.
+template <typename... Ts>
+using variant_move_impl =
+ std::conditional_t<
+ (std::is_move_constructible<variant_base_impl<Ts...>>::value &&
+ std::is_move_assignable<variant_base_impl<Ts...>>::value) ||
+ !variant_storage<Ts...>::move_construct_supported,
+ variant_base_impl<Ts...>,
+ variant_move_impl_non_trivial<Ts...>>;
+
+// Implements non-trivial copy-construction and copy-assignment.
+template <typename... Ts>
+class variant_copy_impl_non_trivial : protected variant_move_impl<Ts...> {
+ using base = variant_move_impl<Ts...>;
+
+public:
+ using base::base;
+
+ variant_copy_impl_non_trivial(
+ const variant_copy_impl_non_trivial& other) {
+ index_ = other.index_;
+ storage_.copy_construct_from(index_, other.storage_);
+ }
+
+ variant_copy_impl_non_trivial(
+ variant_copy_impl_non_trivial&&) = default;
+
+ variant_copy_impl_non_trivial& operator=(
+ const variant_copy_impl_non_trivial& other) {
+ if (&other == this)
+ return *this;
+ storage_.destroy(index_);
+ index_ = other.index_;
+ storage_.copy_construct_from(index_, other.storage_);
+ return *this;
+ }
+
+ variant_copy_impl_non_trivial& operator=(
+ variant_copy_impl_non_trivial&&) = default;
+
+protected:
+ using base::index_;
+ using base::storage_;
+};
+
+// Selects an appropriate variant base class for copying.
+// Use the base impl if the type is trivially
+template <typename... Ts>
+using variant_copy_impl =
+ std::conditional_t<
+ (std::is_copy_constructible<variant_move_impl<Ts...>>::value &&
+ std::is_copy_assignable<variant_move_impl<Ts...>>::value) ||
+ !variant_storage<Ts...>::copy_construct_supported,
+ variant_move_impl<Ts...>,
+ variant_copy_impl_non_trivial<Ts...>>;
+
+// Actual variant type.
+template <typename... Ts>
+class variant : private variant_copy_impl<Ts...> {
+ using base = variant_copy_impl<Ts...>;
+
+public:
+ constexpr variant() = default;
+
+ template <size_t index, typename... Args>
+ explicit constexpr variant(in_place_index_t<index> i, Args&&... args)
+ : base(i, std::forward<Args>(args)...) {}
+
+ variant(const variant&) = default;
+ variant(variant&&) = default;
+ ~variant() = default;
+
+ variant& operator=(const variant&) = default;
+ variant& operator=(variant&&) = default;
+
+ constexpr size_t index() const { return index_; }
+
+ template <size_t index>
+ constexpr auto& get() {
+ assert(index_ == index);
+ return storage_.get(in_place_index<index>);
+ }
+
+ template <size_t index>
+ constexpr const auto& get() const {
+ assert(index_ == index);
+ return storage_.get(in_place_index<index>);
+ }
+
+ template <size_t index, typename... Args>
+ auto& emplace(Args&&... args) {
+ this->destroy();
+ index_ = index;
+ return storage_.emplace(in_place_index<index>,
+ std::forward<Args>(args)...);
+ }
+
+ void swap(variant& other) {
+ using std::swap;
+ if (&other == this)
+ return;
+ if (index_ == other.index_) {
+ storage_.swap(index_, other.storage_);
+ } else {
+ variant temp(std::move(*this));
+ *this = std::move(other);
+ other = std::move(temp);
+ }
+ }
+
+private:
+ using base::index_;
+ using base::storage_;
+};
+
+// Swaps variants.
+template <typename... Ts>
+void swap(variant<Ts...>& a, variant<Ts...>& b) {
+ a.swap(b);
+}
+
+// Gets the type of a variant alternative with the given index.
+template <size_t index, typename Variant>
+struct variant_alternative;
+
+template <size_t index, typename T0, typename... Ts>
+struct variant_alternative<index, variant<T0, Ts...>>
+ : variant_alternative<index - 1, variant<Ts...>> {};
+
+template <typename T0, typename... Ts>
+struct variant_alternative<0, variant<T0, Ts...>> {
+ using type = T0;
+};
+
+template <size_t index, typename Variant>
+using variant_alternative_t = typename variant_alternative<index, Variant>::type;
+
+} // namespace internal
+} // namespace fit
+
+#endif // LIB_FIT_VARIANT_H_
diff --git a/pkg/fit/meta.json b/pkg/fit/meta.json
new file mode 100644
index 0000000..db86eb9
--- /dev/null
+++ b/pkg/fit/meta.json
@@ -0,0 +1,57 @@
+{
+ "banjo_deps": [],
+ "deps": [],
+ "fidl_deps": [],
+ "files": [
+ "pkg/fit/promise.cpp",
+ "pkg/fit/scheduler.cpp",
+ "pkg/fit/sequencer.cpp",
+ "pkg/fit/single_threaded_executor.cpp",
+ "pkg/fit/include/lib/fit/bridge.h",
+ "pkg/fit/include/lib/fit/bridge_internal.h",
+ "pkg/fit/include/lib/fit/defer.h",
+ "pkg/fit/include/lib/fit/function.h",
+ "pkg/fit/include/lib/fit/function_internal.h",
+ "pkg/fit/include/lib/fit/function_traits.h",
+ "pkg/fit/include/lib/fit/nullable.h",
+ "pkg/fit/include/lib/fit/optional.h",
+ "pkg/fit/include/lib/fit/promise.h",
+ "pkg/fit/include/lib/fit/promise_internal.h",
+ "pkg/fit/include/lib/fit/result.h",
+ "pkg/fit/include/lib/fit/scheduler.h",
+ "pkg/fit/include/lib/fit/sequencer.h",
+ "pkg/fit/include/lib/fit/single_threaded_executor.h",
+ "pkg/fit/include/lib/fit/thread_safety.h",
+ "pkg/fit/include/lib/fit/traits.h",
+ "pkg/fit/include/lib/fit/variant.h"
+ ],
+ "headers": [
+ "pkg/fit/include/lib/fit/bridge.h",
+ "pkg/fit/include/lib/fit/bridge_internal.h",
+ "pkg/fit/include/lib/fit/defer.h",
+ "pkg/fit/include/lib/fit/function.h",
+ "pkg/fit/include/lib/fit/function_internal.h",
+ "pkg/fit/include/lib/fit/function_traits.h",
+ "pkg/fit/include/lib/fit/nullable.h",
+ "pkg/fit/include/lib/fit/optional.h",
+ "pkg/fit/include/lib/fit/promise.h",
+ "pkg/fit/include/lib/fit/promise_internal.h",
+ "pkg/fit/include/lib/fit/result.h",
+ "pkg/fit/include/lib/fit/scheduler.h",
+ "pkg/fit/include/lib/fit/sequencer.h",
+ "pkg/fit/include/lib/fit/single_threaded_executor.h",
+ "pkg/fit/include/lib/fit/thread_safety.h",
+ "pkg/fit/include/lib/fit/traits.h",
+ "pkg/fit/include/lib/fit/variant.h"
+ ],
+ "include_dir": "pkg/fit/include",
+ "name": "fit",
+ "root": "pkg/fit",
+ "sources": [
+ "pkg/fit/promise.cpp",
+ "pkg/fit/scheduler.cpp",
+ "pkg/fit/sequencer.cpp",
+ "pkg/fit/single_threaded_executor.cpp"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/fit/promise.cpp b/pkg/fit/promise.cpp
new file mode 100644
index 0000000..8309f00
--- /dev/null
+++ b/pkg/fit/promise.cpp
@@ -0,0 +1,57 @@
+// 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.
+
+// Can't compile this for Zircon userspace yet since libstdc++ isn't available.
+#ifndef FIT_NO_STD_FOR_ZIRCON_USERSPACE
+
+#include <lib/fit/promise.h>
+
+namespace fit {
+
+suspended_task::suspended_task(const suspended_task& other)
+ : resolver_(other.resolver_),
+ ticket_(resolver_ ? resolver_->duplicate_ticket(other.ticket_) : 0) {}
+
+suspended_task::suspended_task(suspended_task&& other)
+ : resolver_(other.resolver_), ticket_(other.ticket_) {
+ other.resolver_ = nullptr;
+}
+
+suspended_task::~suspended_task() {
+ reset();
+}
+
+void suspended_task::resolve(bool resume_task) {
+ if (resolver_) {
+ // Move the ticket to the stack to guard against possible re-entrance
+ // occurring as a side-effect of the task's own destructor running.
+ resolver* cached_resolver = resolver_;
+ ticket cached_ticket = ticket_;
+ resolver_ = nullptr;
+ cached_resolver->resolve_ticket(cached_ticket, resume_task);
+ }
+}
+
+suspended_task& suspended_task::operator=(const suspended_task& other) {
+ if (this != &other) {
+ reset();
+ resolver_ = other.resolver_;
+ ticket_ = resolver_ ? resolver_->duplicate_ticket(other.ticket_) : 0;
+ }
+ return *this;
+}
+
+suspended_task& suspended_task::operator=(suspended_task&& other) {
+ if (this != &other) {
+ reset();
+ resolver_ = other.resolver_;
+ ticket_ = other.ticket_;
+ other.resolver_ = nullptr;
+ }
+ return *this;
+}
+
+} // namespace fit
+
+#endif // FIT_NO_STD_FOR_ZIRCON_USERSPACE
diff --git a/pkg/fit/scheduler.cpp b/pkg/fit/scheduler.cpp
new file mode 100644
index 0000000..69acc03
--- /dev/null
+++ b/pkg/fit/scheduler.cpp
@@ -0,0 +1,128 @@
+// 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.
+
+// Can't compile this for Zircon userspace yet since libstdc++ isn't available.
+#ifndef FIT_NO_STD_FOR_ZIRCON_USERSPACE
+
+#include <lib/fit/scheduler.h>
+
+#include <map>
+#include <queue>
+#include <utility>
+
+namespace fit {
+namespace subtle {
+
+scheduler::scheduler() = default;
+
+scheduler::~scheduler() = default;
+
+void scheduler::schedule_task(pending_task task) {
+ assert(task);
+ runnable_tasks_.push(std::move(task));
+}
+
+suspended_task::ticket scheduler::obtain_ticket(uint32_t initial_refs) {
+ suspended_task::ticket ticket = next_ticket_++;
+ tickets_.emplace(ticket, ticket_record(initial_refs));
+ return ticket;
+}
+
+void scheduler::finalize_ticket(suspended_task::ticket ticket,
+ pending_task* task) {
+ auto it = tickets_.find(ticket);
+ assert(it != tickets_.end());
+ assert(!it->second.task);
+ assert(it->second.ref_count > 0);
+ assert(task);
+
+ it->second.ref_count--;
+ if (!*task) {
+ // task already finished
+ } else if (it->second.was_resumed) {
+ // task immediately became runnable
+ runnable_tasks_.push(std::move(*task));
+ } else if (it->second.ref_count > 0) {
+ // task remains suspended
+ it->second.task = std::move(*task);
+ suspended_task_count_++;
+ } // else, task was abandoned and caller retains ownership of it
+ if (it->second.ref_count == 0) {
+ tickets_.erase(it);
+ }
+}
+
+void scheduler::duplicate_ticket(suspended_task::ticket ticket) {
+ auto it = tickets_.find(ticket);
+ assert(it != tickets_.end());
+ assert(it->second.ref_count > 0);
+
+ it->second.ref_count++;
+ assert(it->second.ref_count != 0); // did we really make 4 billion refs?!
+}
+
+pending_task scheduler::release_ticket(suspended_task::ticket ticket) {
+ auto it = tickets_.find(ticket);
+ assert(it != tickets_.end());
+ assert(it->second.ref_count > 0);
+
+ it->second.ref_count--;
+ if (it->second.ref_count == 0) {
+ pending_task task = std::move(it->second.task);
+ if (task) {
+ assert(suspended_task_count_ > 0);
+ suspended_task_count_--;
+ }
+ tickets_.erase(it);
+ return task;
+ }
+ return pending_task();
+}
+
+bool scheduler::resume_task_with_ticket(suspended_task::ticket ticket) {
+ auto it = tickets_.find(ticket);
+ assert(it != tickets_.end());
+ assert(it->second.ref_count > 0);
+
+ bool did_resume = false;
+ it->second.ref_count--;
+ if (!it->second.was_resumed) {
+ it->second.was_resumed = true;
+ if (it->second.task) {
+ did_resume = true;
+ assert(suspended_task_count_ > 0);
+ suspended_task_count_--;
+ runnable_tasks_.push(std::move(it->second.task));
+ }
+ }
+ if (it->second.ref_count == 0) {
+ tickets_.erase(it);
+ }
+ return did_resume;
+}
+
+void scheduler::take_runnable_tasks(task_queue* tasks) {
+ assert(tasks && tasks->empty());
+ runnable_tasks_.swap(*tasks);
+}
+
+void scheduler::take_all_tasks(task_queue* tasks) {
+ assert(tasks && tasks->empty());
+
+ runnable_tasks_.swap(*tasks);
+ if (suspended_task_count_ > 0) {
+ for (auto& item : tickets_) {
+ if (item.second.task) {
+ assert(suspended_task_count_ > 0);
+ suspended_task_count_--;
+ tasks->push(std::move(item.second.task));
+ }
+ }
+ }
+}
+
+} // namespace subtle
+} // namespace fit
+
+#endif // FIT_NO_STD_FOR_ZIRCON_USERSPACE
diff --git a/pkg/fit/sequencer.cpp b/pkg/fit/sequencer.cpp
new file mode 100644
index 0000000..687db67
--- /dev/null
+++ b/pkg/fit/sequencer.cpp
@@ -0,0 +1,31 @@
+// 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.
+
+// Can't compile this for Zircon userspace yet since libstdc++ isn't available.
+#ifndef FIT_NO_STD_FOR_ZIRCON_USERSPACE
+
+#include <lib/fit/sequencer.h>
+
+namespace fit {
+
+sequencer::sequencer() {
+ // Capture a new consumer and intentionally abandon its associated
+ // completer so that a promise chained onto the consumer using
+ // |promise_or()| will become immediately runnable.
+ fit::bridge<> bridge;
+ prior_ = std::move(bridge.consumer());
+}
+
+sequencer::~sequencer() = default;
+
+fit::consumer<> sequencer::swap_prior(fit::consumer<> new_prior) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ fit::consumer<> old_prior = std::move(prior_);
+ prior_ = std::move(new_prior);
+ return old_prior;
+}
+
+} // namespace fit
+
+#endif // FIT_NO_STD_FOR_ZIRCON_USERSPACE
diff --git a/pkg/fit/single_threaded_executor.cpp b/pkg/fit/single_threaded_executor.cpp
new file mode 100644
index 0000000..ba738d0
--- /dev/null
+++ b/pkg/fit/single_threaded_executor.cpp
@@ -0,0 +1,240 @@
+// 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.
+
+// Can't compile this for Zircon userspace yet since libstdc++ isn't available.
+#ifndef FIT_NO_STD_FOR_ZIRCON_USERSPACE
+
+#include <condition_variable>
+#include <mutex>
+
+#include <lib/fit/single_threaded_executor.h>
+#include <lib/fit/thread_safety.h>
+
+namespace fit {
+
+// The dispatcher runs tasks and provides the suspended task resolver.
+//
+// The lifetime of this object is somewhat complex since there are pointers
+// to it from multiple sources which are released in different ways.
+//
+// - |single_threaded_executor| holds a pointer in |dispatcher_| which it releases
+// after calling |shutdown()| to inform the dispatcher of its own demise
+// - |suspended_task| holds a pointer to the dispatcher's resolver
+// interface and the number of outstanding pointers corresponds to the
+// number of outstanding suspended task tickets tracked by |scheduler_|.
+//
+// The dispatcher deletes itself once all pointers have been released.
+class single_threaded_executor::dispatcher_impl final
+ : public suspended_task::resolver {
+public:
+ dispatcher_impl();
+
+ void shutdown();
+ void schedule_task(pending_task task);
+ void run(context_impl& context);
+ suspended_task suspend_current_task();
+
+ suspended_task::ticket duplicate_ticket(
+ suspended_task::ticket ticket) override;
+ void resolve_ticket(
+ suspended_task::ticket ticket, bool resume_task) override;
+
+private:
+ ~dispatcher_impl() override;
+
+ void wait_for_runnable_tasks(
+ fit::subtle::scheduler::task_queue* out_tasks);
+ void run_task(pending_task* task, context& context);
+
+ suspended_task::ticket current_task_ticket_ = 0;
+ std::condition_variable wake_;
+
+ // A bunch of state that is guarded by a mutex.
+ struct {
+ std::mutex mutex_;
+ bool was_shutdown_ FIT_GUARDED(mutex_) = false;
+ bool need_wake_ FIT_GUARDED(mutex_) = false;
+ fit::subtle::scheduler scheduler_ FIT_GUARDED(mutex_);
+ } guarded_;
+};
+
+single_threaded_executor::single_threaded_executor()
+ : context_(this), dispatcher_(new dispatcher_impl()) {}
+
+single_threaded_executor::~single_threaded_executor() {
+ dispatcher_->shutdown();
+}
+
+void single_threaded_executor::schedule_task(pending_task task) {
+ assert(task);
+ dispatcher_->schedule_task(std::move(task));
+}
+
+void single_threaded_executor::run() {
+ dispatcher_->run(context_);
+}
+
+single_threaded_executor::context_impl::context_impl(single_threaded_executor* executor)
+ : executor_(executor) {}
+
+single_threaded_executor::context_impl::~context_impl() = default;
+
+single_threaded_executor* single_threaded_executor::context_impl::executor() const {
+ return executor_;
+}
+
+suspended_task single_threaded_executor::context_impl::suspend_task() {
+ return executor_->dispatcher_->suspend_current_task();
+}
+
+single_threaded_executor::dispatcher_impl::dispatcher_impl() = default;
+
+single_threaded_executor::dispatcher_impl::~dispatcher_impl() {
+ std::lock_guard<std::mutex> lock(guarded_.mutex_);
+ assert(guarded_.was_shutdown_);
+ assert(!guarded_.scheduler_.has_runnable_tasks());
+ assert(!guarded_.scheduler_.has_suspended_tasks());
+ assert(!guarded_.scheduler_.has_outstanding_tickets());
+}
+
+void single_threaded_executor::dispatcher_impl::shutdown() {
+ fit::subtle::scheduler::task_queue tasks; // drop outside of the lock
+ {
+ std::lock_guard<std::mutex> lock(guarded_.mutex_);
+ assert(!guarded_.was_shutdown_);
+ guarded_.was_shutdown_ = true;
+ guarded_.scheduler_.take_all_tasks(&tasks);
+ if (guarded_.scheduler_.has_outstanding_tickets()) {
+ return; // can't delete self yet
+ }
+ }
+
+ // Must destroy self outside of the lock.
+ delete this;
+}
+
+void single_threaded_executor::dispatcher_impl::schedule_task(pending_task task) {
+ {
+ std::lock_guard<std::mutex> lock(guarded_.mutex_);
+ assert(!guarded_.was_shutdown_);
+ guarded_.scheduler_.schedule_task(std::move(task));
+ if (!guarded_.need_wake_) {
+ return; // don't need to wake
+ }
+ guarded_.need_wake_ = false;
+ }
+
+ // It is more efficient to notify outside the lock.
+ wake_.notify_one();
+}
+
+void single_threaded_executor::dispatcher_impl::run(context_impl& context) {
+ fit::subtle::scheduler::task_queue tasks;
+ for (;;) {
+ wait_for_runnable_tasks(&tasks);
+ if (tasks.empty()) {
+ return; // all done!
+ }
+
+ do {
+ run_task(&tasks.front(), context);
+ tasks.pop(); // the task may be destroyed here if it was not suspended
+ } while (!tasks.empty());
+ }
+}
+
+// Must only be called while |run_task()| is running a task.
+// This happens when the task's continuation calls |context::suspend_task()|
+// upon the context it received as an argument.
+suspended_task single_threaded_executor::dispatcher_impl::suspend_current_task() {
+ std::lock_guard<std::mutex> lock(guarded_.mutex_);
+ assert(!guarded_.was_shutdown_);
+ if (current_task_ticket_ == 0) {
+ current_task_ticket_ = guarded_.scheduler_.obtain_ticket(
+ 2 /*initial_refs*/);
+ } else {
+ guarded_.scheduler_.duplicate_ticket(current_task_ticket_);
+ }
+ return suspended_task(this, current_task_ticket_);
+}
+
+// Unfortunately std::unique_lock does not support thread-safety annotations
+void single_threaded_executor::dispatcher_impl::wait_for_runnable_tasks(
+ fit::subtle::scheduler::task_queue* out_tasks) FIT_NO_THREAD_SAFETY_ANALYSIS {
+ std::unique_lock<std::mutex> lock(guarded_.mutex_);
+ for (;;) {
+ assert(!guarded_.was_shutdown_);
+ guarded_.scheduler_.take_runnable_tasks(out_tasks);
+ if (!out_tasks->empty()) {
+ return; // got some tasks
+ }
+ if (!guarded_.scheduler_.has_suspended_tasks()) {
+ return; // all done!
+ }
+ guarded_.need_wake_ = true;
+ wake_.wait(lock);
+ guarded_.need_wake_ = false;
+ }
+}
+
+void single_threaded_executor::dispatcher_impl::run_task(pending_task* task,
+ context& context) {
+ assert(current_task_ticket_ == 0);
+ const bool finished = (*task)(context);
+ assert(!*task == finished);
+ (void)finished;
+ if (current_task_ticket_ == 0) {
+ return; // task was not suspended, no ticket was produced
+ }
+
+ std::lock_guard<std::mutex> lock(guarded_.mutex_);
+ assert(!guarded_.was_shutdown_);
+ guarded_.scheduler_.finalize_ticket(current_task_ticket_, task);
+ current_task_ticket_ = 0;
+}
+
+suspended_task::ticket single_threaded_executor::dispatcher_impl::duplicate_ticket(
+ suspended_task::ticket ticket) {
+ std::lock_guard<std::mutex> lock(guarded_.mutex_);
+ guarded_.scheduler_.duplicate_ticket(ticket);
+ return ticket;
+}
+
+void single_threaded_executor::dispatcher_impl::resolve_ticket(
+ suspended_task::ticket ticket, bool resume_task) {
+ pending_task abandoned_task; // drop outside of the lock
+ bool do_wake = false;
+ {
+ std::lock_guard<std::mutex> lock(guarded_.mutex_);
+ if (resume_task) {
+ guarded_.scheduler_.resume_task_with_ticket(ticket);
+ } else {
+ abandoned_task = guarded_.scheduler_.release_ticket(ticket);
+ }
+ if (guarded_.was_shutdown_) {
+ assert(!guarded_.need_wake_);
+ if (guarded_.scheduler_.has_outstanding_tickets()) {
+ return; // can't shutdown yet
+ }
+ } else if (guarded_.need_wake_ &&
+ (guarded_.scheduler_.has_runnable_tasks() ||
+ !guarded_.scheduler_.has_suspended_tasks())) {
+ guarded_.need_wake_ = false;
+ do_wake = true;
+ } else {
+ return; // nothing else to do
+ }
+ }
+
+ // Must do this outside of the lock.
+ if (do_wake) {
+ wake_.notify_one();
+ } else {
+ delete this;
+ }
+}
+
+} // namespace fit
+
+#endif // FIT_NO_STD_FOR_ZIRCON_USERSPACE
diff --git a/pkg/images_cpp/images.cc b/pkg/images_cpp/images.cc
new file mode 100644
index 0000000..17803c6
--- /dev/null
+++ b/pkg/images_cpp/images.cc
@@ -0,0 +1,84 @@
+// 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/images/cpp/images.h"
+
+#include <zircon/assert.h>
+
+namespace images {
+
+// Overall bits per pixel, across all pixel data in the whole image.
+size_t BitsPerPixel(const fuchsia::images::PixelFormat& pixel_format) {
+ switch (pixel_format) {
+ case fuchsia::images::PixelFormat::BGRA_8:
+ return 4u * 8u;
+ case fuchsia::images::PixelFormat::YUY2:
+ return 2u * 8u;
+ case fuchsia::images::PixelFormat::NV12:
+ return 12;
+ case fuchsia::images::PixelFormat::YV12:
+ return 12;
+ }
+ ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format));
+ return 0;
+}
+
+size_t StrideBytesPerWidthPixel(
+ const fuchsia::images::PixelFormat& pixel_format) {
+ switch (pixel_format) {
+ case fuchsia::images::PixelFormat::BGRA_8:
+ return 4u;
+ case fuchsia::images::PixelFormat::YUY2:
+ return 2u;
+ case fuchsia::images::PixelFormat::NV12:
+ return 1u;
+ case fuchsia::images::PixelFormat::YV12:
+ return 1u;
+ }
+ ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format));
+ return 0;
+}
+
+size_t MaxSampleAlignment(const fuchsia::images::PixelFormat& pixel_format) {
+ switch (pixel_format) {
+ case fuchsia::images::PixelFormat::BGRA_8:
+ return 4u;
+ case fuchsia::images::PixelFormat::YUY2:
+ return 2u;
+ case fuchsia::images::PixelFormat::NV12:
+ // In the sense that line stride "must preserve pixel alignment", which is
+ // what MaxSampleAlignment() is used for, NV12 ~~has (very roughly
+ // speaking) a pixel alignment of 2, ~~because the width of the Y plane in
+ // this implementation must be even, and ~because the interleaved UV data
+ // after the planar Y data is 2 bytes per sample, so we may as well
+ // require UV samples to remain aligned UV line to UV line.
+ return 2u;
+ case fuchsia::images::PixelFormat::YV12:
+ // In the sense that line stride "must preserve pixel alignment", which is
+ // what MaxSampleAlignment() is used for, YV12 ~~has (very roughly
+ // speaking) a pixel alignment of 2, ~~because the width of the Y plane in
+ // this implementation must be even.
+ return 2u;
+ }
+ ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format));
+ return 0;
+}
+
+size_t ImageSize(const fuchsia::images::ImageInfo& image_info) {
+ ZX_DEBUG_ASSERT(image_info.tiling == fuchsia::images::Tiling::LINEAR);
+ switch (image_info.pixel_format) {
+ case fuchsia::images::PixelFormat::BGRA_8:
+ case fuchsia::images::PixelFormat::YUY2:
+ return image_info.height * image_info.stride;
+ case fuchsia::images::PixelFormat::NV12:
+ return image_info.height * image_info.stride * 3 / 2;
+ case fuchsia::images::PixelFormat::YV12:
+ return image_info.height * image_info.stride * 3 / 2;
+ }
+ ZX_PANIC("Unknown Pixel Format: %d",
+ static_cast<int>(image_info.pixel_format));
+ return 0;
+}
+
+} // namespace images
diff --git a/pkg/images_cpp/include/lib/images/cpp/images.h b/pkg/images_cpp/include/lib/images/cpp/images.h
new file mode 100644
index 0000000..48d75e6
--- /dev/null
+++ b/pkg/images_cpp/include/lib/images/cpp/images.h
@@ -0,0 +1,41 @@
+// 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.
+
+#ifndef LIB_IMAGES_CPP_IMAGES_H_
+#define LIB_IMAGES_CPP_IMAGES_H_
+
+#include <fuchsia/images/cpp/fidl.h>
+
+#include <stdint.h>
+
+namespace images {
+
+// Returns the number of bits per pixel for the given format. This is the bits
+// per pixel in the overall image across all bytes that contain pixel data. For
+// example, NV12 is 12 bits per pixel.
+size_t BitsPerPixel(const fuchsia::images::PixelFormat& pixel_format);
+
+// This is the number of stride bytes per pixel of width. For formats such as
+// NV12 that separate Y and UV data, this is the number of stride bytes of the Y
+// plane. NV12 has the same stride for the UV data. formats with a different
+// stride for UV data vs Y data are not handled yet.
+size_t StrideBytesPerWidthPixel(
+ const fuchsia::images::PixelFormat& pixel_format);
+
+// Returns the pixel alignment for the given format.
+//
+// This is technically something closer to "max sample alignment" for the given
+// format. For example, NV12 returns 2 here because its UV interleaved data has
+// 2 bytes per sample, despite its Y plane having 1 byte per sample.
+//
+// The stride is required to be divisible by this alignment.
+size_t MaxSampleAlignment(const fuchsia::images::PixelFormat& pixel_format);
+
+// This would be height * stride, if it weren't for formats like NV12, where it
+// isn't. The output is bytes.
+size_t ImageSize(const fuchsia::images::ImageInfo& image_info);
+
+} // namespace images
+
+#endif // LIB_IMAGES_CPP_IMAGES_H_
diff --git a/pkg/images_cpp/meta.json b/pkg/images_cpp/meta.json
new file mode 100644
index 0000000..61fdef4
--- /dev/null
+++ b/pkg/images_cpp/meta.json
@@ -0,0 +1,16 @@
+{
+ "deps": [],
+ "fidl_deps": [
+ "fuchsia.images"
+ ],
+ "headers": [
+ "pkg/images_cpp/include/lib/images/cpp/images.h"
+ ],
+ "include_dir": "pkg/images_cpp/include",
+ "name": "images_cpp",
+ "root": "pkg/images_cpp",
+ "sources": [
+ "pkg/images_cpp/images.cc"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/memfs/include/lib/memfs/memfs.h b/pkg/memfs/include/lib/memfs/memfs.h
new file mode 100644
index 0000000..7f49a96
--- /dev/null
+++ b/pkg/memfs/include/lib/memfs/memfs.h
@@ -0,0 +1,64 @@
+// Copyright 2017 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.
+
+#ifndef LIB_MEMFS_INCLUDE_LIB_MEMFS_MEMFS_H_
+#define LIB_MEMFS_INCLUDE_LIB_MEMFS_MEMFS_H_
+
+#include <lib/async/dispatcher.h>
+#include <lib/sync/completion.h>
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+typedef struct memfs_filesystem memfs_filesystem_t;
+
+// Given an async dispatcher, create an in-memory filesystem.
+//
+// Returns the MemFS filesystem object in |out_fs|. This object
+// must be freed by memfs_free_filesystem.
+//
+// Returns a handle to the root directory in |out_root|.
+__EXPORT zx_status_t memfs_create_filesystem(async_dispatcher_t* dispatcher,
+ memfs_filesystem_t** out_fs,
+ zx_handle_t* out_root);
+
+// Same as memfs_create_filesystem, but with an extra |max_num_pages| option.
+//
+// Specify the maximum number of pages available to the fs via |max_num_pages|.
+// This puts a bound on memory consumption.
+__EXPORT zx_status_t memfs_create_filesystem_with_page_limit(async_dispatcher_t* dispatcher,
+ size_t max_num_pages,
+ memfs_filesystem_t** out_fs,
+ zx_handle_t* out_root);
+
+// Frees a MemFS filesystem, unmounting any sub-filesystems that
+// may exist.
+//
+// Requires that the async handler dispatcher provided to
+// |memfs_create_filesystem| still be running.
+//
+// Signals the optional argument |unmounted| when memfs has torn down.
+__EXPORT void memfs_free_filesystem(memfs_filesystem_t* fs, sync_completion_t* unmounted);
+
+// Creates an in-memory filesystem and installs it into the local namespace at
+// the given path.
+//
+// Operations on the filesystem are serviced by the given async dispatcher.
+//
+// Returns |ZX_ERR_ALREADY_EXISTS| if |path| already exists in the namespace for
+// this process.
+__EXPORT zx_status_t memfs_install_at(async_dispatcher_t* dispatcher, const char* path);
+
+// Same as memfs_install_at, but with an extra |max_num_pages| option.
+//
+// Specify the maximum number of pages available to the fs via |max_num_pages|.
+// This puts a bound on memory consumption.
+__EXPORT zx_status_t memfs_install_at_with_page_limit(async_dispatcher_t* dispatcher,
+ size_t max_num_pages,
+ const char* path);
+
+__END_CDECLS
+
+#endif // LIB_MEMFS_INCLUDE_LIB_MEMFS_MEMFS_H_
diff --git a/pkg/memfs/meta.json b/pkg/memfs/meta.json
new file mode 100644
index 0000000..1492c3e
--- /dev/null
+++ b/pkg/memfs/meta.json
@@ -0,0 +1,26 @@
+{
+ "binaries": {
+ "arm64": {
+ "debug": ".build-id/bf/c0f8386f84cd2c91ccae603bc6910bdbaf7f29.debug",
+ "dist": "arch/arm64/dist/libmemfs.so",
+ "link": "arch/arm64/lib/libmemfs.so"
+ },
+ "x64": {
+ "debug": ".build-id/7b/1c4f16beffe8102284ef4860a2a92696ed5045.debug",
+ "dist": "arch/x64/dist/libmemfs.so",
+ "link": "arch/x64/lib/libmemfs.so"
+ }
+ },
+ "deps": [
+ "fdio",
+ "trace-engine"
+ ],
+ "format": "shared",
+ "headers": [
+ "pkg/memfs/include/lib/memfs/memfs.h"
+ ],
+ "include_dir": "pkg/memfs/include",
+ "name": "memfs",
+ "root": "pkg/memfs",
+ "type": "cc_prebuilt_library"
+}
\ No newline at end of file
diff --git a/pkg/scenic_cpp/commands.cc b/pkg/scenic_cpp/commands.cc
new file mode 100644
index 0000000..4991905
--- /dev/null
+++ b/pkg/scenic_cpp/commands.cc
@@ -0,0 +1,1300 @@
+// Copyright 2017 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/ui/scenic/cpp/commands.h"
+
+#include <array>
+
+#include <zircon/assert.h>
+
+namespace scenic {
+
+fuchsia::ui::scenic::Command NewCommand(fuchsia::ui::gfx::Command command) {
+ fuchsia::ui::scenic::Command scenic_command;
+ scenic_command.set_gfx(std::move(command));
+ return scenic_command;
+}
+
+fuchsia::ui::scenic::Command NewCommand(fuchsia::ui::input::Command command) {
+ fuchsia::ui::scenic::Command scenic_command;
+ scenic_command.set_input(std::move(command));
+ return scenic_command;
+}
+
+// Helper function for all resource creation functions.
+fuchsia::ui::gfx::Command NewCreateResourceCmd(
+ uint32_t id, fuchsia::ui::gfx::ResourceArgs resource) {
+ fuchsia::ui::gfx::CreateResourceCmd create_resource;
+ create_resource.id = id;
+ create_resource.resource = std::move(resource);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_create_resource(std::move(create_resource));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewCreateMemoryCmd(
+ uint32_t id, zx::vmo vmo, uint64_t allocation_size,
+ fuchsia::images::MemoryType memory_type) {
+ fuchsia::ui::gfx::MemoryArgs memory;
+ memory.vmo = std::move(vmo);
+ memory.allocation_size = allocation_size;
+ memory.memory_type = memory_type;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_memory(std::move(memory));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateImageCmd(uint32_t id, uint32_t memory_id,
+ uint32_t memory_offset,
+ fuchsia::images::ImageInfo info) {
+ fuchsia::ui::gfx::ImageArgs image;
+ image.memory_id = memory_id;
+ image.memory_offset = memory_offset;
+ image.info = std::move(info);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_image(std::move(image));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateImagePipeCmd(
+ uint32_t id, ::fidl::InterfaceRequest<fuchsia::images::ImagePipe> request) {
+ fuchsia::ui::gfx::ImagePipeArgs image_pipe;
+ image_pipe.image_pipe_request = std::move(request);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_image_pipe(std::move(image_pipe));
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateImageCmd(
+ uint32_t id, uint32_t memory_id, uint32_t memory_offset,
+ fuchsia::images::PixelFormat format,
+ fuchsia::images::ColorSpace color_space, fuchsia::images::Tiling tiling,
+ uint32_t width, uint32_t height, uint32_t stride) {
+ fuchsia::images::ImageInfo info;
+ info.pixel_format = format;
+ info.color_space = color_space;
+ info.tiling = tiling;
+ info.width = width;
+ info.height = height;
+ info.stride = stride;
+ return NewCreateImageCmd(id, memory_id, memory_offset, std::move(info));
+}
+
+fuchsia::ui::gfx::Command NewCreateBufferCmd(uint32_t id, uint32_t memory_id,
+ uint32_t memory_offset,
+ uint32_t num_bytes) {
+ fuchsia::ui::gfx::BufferArgs buffer;
+ buffer.memory_id = memory_id;
+ buffer.memory_offset = memory_offset;
+ buffer.num_bytes = num_bytes;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_buffer(std::move(buffer));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateCompositorCmd(uint32_t id) {
+ fuchsia::ui::gfx::CompositorArgs compositor;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_compositor(std::move(compositor));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateDisplayCompositorCmd(uint32_t id) {
+ fuchsia::ui::gfx::DisplayCompositorArgs display_compositor;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_display_compositor(std::move(display_compositor));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateLayerStackCmd(uint32_t id) {
+ fuchsia::ui::gfx::LayerStackArgs layer_stack;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_layer_stack(std::move(layer_stack));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateLayerCmd(uint32_t id) {
+ fuchsia::ui::gfx::LayerArgs layer;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_layer(std::move(layer));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateSceneCmd(uint32_t id) {
+ fuchsia::ui::gfx::SceneArgs scene;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_scene(std::move(scene));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateCameraCmd(uint32_t id, uint32_t scene_id) {
+ fuchsia::ui::gfx::CameraArgs camera;
+ camera.scene_id = scene_id;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_camera(std::move(camera));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateStereoCameraCmd(uint32_t id,
+ uint32_t scene_id) {
+ fuchsia::ui::gfx::StereoCameraArgs stereo_camera;
+ stereo_camera.scene_id = scene_id;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_stereo_camera(std::move(stereo_camera));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateRendererCmd(uint32_t id) {
+ fuchsia::ui::gfx::RendererArgs renderer;
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_renderer(std::move(renderer));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateAmbientLightCmd(uint32_t id) {
+ fuchsia::ui::gfx::AmbientLightArgs ambient_light;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_ambient_light(std::move(ambient_light));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateDirectionalLightCmd(uint32_t id) {
+ fuchsia::ui::gfx::DirectionalLightArgs directional_light;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_directional_light(std::move(directional_light));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateCircleCmd(uint32_t id, float radius) {
+ fuchsia::ui::gfx::Value radius_value;
+ radius_value.set_vector1(radius);
+
+ fuchsia::ui::gfx::CircleArgs circle;
+ circle.radius = std::move(radius_value);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_circle(std::move(circle));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateRectangleCmd(uint32_t id, float width,
+ float height) {
+ fuchsia::ui::gfx::Value width_value;
+ width_value.set_vector1(width);
+
+ fuchsia::ui::gfx::Value height_value;
+ height_value.set_vector1(height);
+
+ fuchsia::ui::gfx::RectangleArgs rectangle;
+ rectangle.width = std::move(width_value);
+ rectangle.height = std::move(height_value);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_rectangle(std::move(rectangle));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateRoundedRectangleCmd(
+ uint32_t id, float width, float height, float top_left_radius,
+ float top_right_radius, float bottom_right_radius,
+ float bottom_left_radius) {
+ fuchsia::ui::gfx::Value width_value;
+ width_value.set_vector1(width);
+
+ fuchsia::ui::gfx::Value height_value;
+ height_value.set_vector1(height);
+
+ fuchsia::ui::gfx::Value top_left_radius_value;
+ top_left_radius_value.set_vector1(top_left_radius);
+
+ fuchsia::ui::gfx::Value top_right_radius_value;
+ top_right_radius_value.set_vector1(top_right_radius);
+
+ fuchsia::ui::gfx::Value bottom_right_radius_value;
+ bottom_right_radius_value.set_vector1(bottom_right_radius);
+
+ fuchsia::ui::gfx::Value bottom_left_radius_value;
+ bottom_left_radius_value.set_vector1(bottom_left_radius);
+
+ fuchsia::ui::gfx::RoundedRectangleArgs rectangle;
+ rectangle.width = std::move(width_value);
+ rectangle.height = std::move(height_value);
+ rectangle.top_left_radius = std::move(top_left_radius_value);
+ rectangle.top_right_radius = std::move(top_right_radius_value);
+ rectangle.bottom_right_radius = std::move(bottom_right_radius_value);
+ rectangle.bottom_left_radius = std::move(bottom_left_radius_value);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_rounded_rectangle(std::move(rectangle));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateVarCircleCmd(uint32_t id,
+ uint32_t radius_var_id,
+ uint32_t height_var_id) {
+ fuchsia::ui::gfx::Value radius_value;
+ radius_value.set_variable_id(radius_var_id);
+
+ fuchsia::ui::gfx::CircleArgs circle;
+ circle.radius = std::move(radius_value);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_circle(std::move(circle));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateVarRectangleCmd(uint32_t id,
+ uint32_t width_var_id,
+ uint32_t height_var_id) {
+ fuchsia::ui::gfx::Value width_value;
+ width_value.set_variable_id(width_var_id);
+
+ fuchsia::ui::gfx::Value height_value;
+ height_value.set_variable_id(height_var_id);
+
+ fuchsia::ui::gfx::RectangleArgs rectangle;
+ rectangle.width = std::move(width_value);
+ rectangle.height = std::move(height_value);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_rectangle(std::move(rectangle));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateVarRoundedRectangleCmd(
+ uint32_t id, uint32_t width_var_id, uint32_t height_var_id,
+ uint32_t top_left_radius_var_id, uint32_t top_right_radius_var_id,
+ uint32_t bottom_left_radius_var_id, uint32_t bottom_right_radius_var_id) {
+ fuchsia::ui::gfx::Value width_value;
+ width_value.set_variable_id(width_var_id);
+
+ fuchsia::ui::gfx::Value height_value;
+ height_value.set_variable_id(height_var_id);
+
+ fuchsia::ui::gfx::Value top_left_radius_value;
+ top_left_radius_value.set_variable_id(top_left_radius_var_id);
+
+ fuchsia::ui::gfx::Value top_right_radius_value;
+ top_right_radius_value.set_variable_id(top_right_radius_var_id);
+
+ fuchsia::ui::gfx::Value bottom_left_radius_value;
+ bottom_left_radius_value.set_variable_id(bottom_left_radius_var_id);
+
+ fuchsia::ui::gfx::Value bottom_right_radius_value;
+ bottom_right_radius_value.set_variable_id(bottom_right_radius_var_id);
+
+ fuchsia::ui::gfx::RoundedRectangleArgs rectangle;
+ rectangle.width = std::move(width_value);
+ rectangle.height = std::move(height_value);
+ rectangle.top_left_radius = std::move(top_left_radius_value);
+ rectangle.top_right_radius = std::move(top_right_radius_value);
+ rectangle.bottom_left_radius = std::move(bottom_left_radius_value);
+ rectangle.bottom_right_radius = std::move(bottom_right_radius_value);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_rounded_rectangle(std::move(rectangle));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateMeshCmd(uint32_t id) {
+ fuchsia::ui::gfx::MeshArgs mesh;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_mesh(std::move(mesh));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateMaterialCmd(uint32_t id) {
+ fuchsia::ui::gfx::MaterialArgs material;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_material(std::move(material));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateClipNodeCmd(uint32_t id) {
+ fuchsia::ui::gfx::ClipNodeArgs node;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_clip_node(std::move(node));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateEntityNodeCmd(uint32_t id) {
+ fuchsia::ui::gfx::EntityNodeArgs node;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_entity_node(std::move(node));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateOpacityNodeCmd(uint32_t id) {
+ fuchsia::ui::gfx::OpacityNodeArgs node;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_opacity_node(std::move(node));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateShapeNodeCmd(uint32_t id) {
+ fuchsia::ui::gfx::ShapeNodeArgs node;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_shape_node(std::move(node));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateViewCmd(uint32_t id, zx::eventpair token,
+ const std::string& debug_name) {
+ ZX_DEBUG_ASSERT(token);
+ fuchsia::ui::gfx::ViewArgs view;
+ view.token = std::move(token);
+ view.debug_name = debug_name;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_view(std::move(view));
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateViewHolderCmd(
+ uint32_t id, zx::eventpair token, const std::string& debug_name) {
+ ZX_DEBUG_ASSERT(token);
+ fuchsia::ui::gfx::ViewHolderArgs view_holder;
+ view_holder.token = std::move(token);
+ view_holder.debug_name = debug_name;
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_view_holder(std::move(view_holder));
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewCreateVariableCmd(uint32_t id,
+ fuchsia::ui::gfx::Value value) {
+ fuchsia::ui::gfx::VariableArgs variable;
+ switch (value.Which()) {
+ case ::fuchsia::ui::gfx::Value::Tag::kVector1:
+ variable.type = fuchsia::ui::gfx::ValueType::kVector1;
+ break;
+ case ::fuchsia::ui::gfx::Value::Tag::kVector2:
+ variable.type = fuchsia::ui::gfx::ValueType::kVector2;
+ break;
+ case ::fuchsia::ui::gfx::Value::Tag::kVector3:
+ variable.type = fuchsia::ui::gfx::ValueType::kVector3;
+ break;
+ case ::fuchsia::ui::gfx::Value::Tag::kVector4:
+ variable.type = fuchsia::ui::gfx::ValueType::kVector4;
+ break;
+ case fuchsia::ui::gfx::Value::Tag::kMatrix4x4:
+ variable.type = fuchsia::ui::gfx::ValueType::kMatrix4;
+ break;
+ case fuchsia::ui::gfx::Value::Tag::kColorRgba:
+ variable.type = fuchsia::ui::gfx::ValueType::kColorRgba;
+ break;
+ case fuchsia::ui::gfx::Value::Tag::kColorRgb:
+ variable.type = fuchsia::ui::gfx::ValueType::kColorRgb;
+ break;
+ case fuchsia::ui::gfx::Value::Tag::kDegrees:
+ variable.type = fuchsia::ui::gfx::ValueType::kVector1;
+ break;
+ case fuchsia::ui::gfx::Value::Tag::kTransform:
+ variable.type = fuchsia::ui::gfx::ValueType::kFactoredTransform;
+ break;
+ case fuchsia::ui::gfx::Value::Tag::kQuaternion:
+ variable.type = fuchsia::ui::gfx::ValueType::kQuaternion;
+ break;
+ case fuchsia::ui::gfx::Value::Tag::kVariableId:
+ // A variable's initial value cannot be another variable.
+ return fuchsia::ui::gfx::Command();
+ case fuchsia::ui::gfx::Value::Tag::Invalid:
+ return fuchsia::ui::gfx::Command();
+ }
+ variable.initial_value = std::move(value);
+
+ fuchsia::ui::gfx::ResourceArgs resource;
+ resource.set_variable(std::move(variable));
+
+ return NewCreateResourceCmd(id, std::move(resource));
+}
+
+fuchsia::ui::gfx::Command NewReleaseResourceCmd(uint32_t id) {
+ fuchsia::ui::gfx::ReleaseResourceCmd release_resource;
+ release_resource.id = id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_release_resource(std::move(release_resource));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewExportResourceCmd(uint32_t resource_id,
+ zx::eventpair export_token) {
+ ZX_DEBUG_ASSERT(export_token);
+
+ fuchsia::ui::gfx::ExportResourceCmd export_resource;
+ export_resource.id = resource_id;
+ export_resource.token = std::move(export_token);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_export_resource(std::move(export_resource));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewImportResourceCmd(
+ uint32_t resource_id, fuchsia::ui::gfx::ImportSpec spec,
+ zx::eventpair import_token) {
+ ZX_DEBUG_ASSERT(import_token);
+
+ fuchsia::ui::gfx::ImportResourceCmd import_resource;
+ import_resource.id = resource_id;
+ import_resource.token = std::move(import_token);
+ import_resource.spec = spec;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_import_resource(std::move(import_resource));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewExportResourceCmdAsRequest(
+ uint32_t resource_id, zx::eventpair* out_import_token) {
+ ZX_DEBUG_ASSERT(out_import_token);
+ ZX_DEBUG_ASSERT(!*out_import_token);
+
+ zx::eventpair export_token;
+ zx_status_t status =
+ zx::eventpair::create(0u, &export_token, out_import_token);
+ ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "event pair create failed: status=%d",
+ status);
+ return NewExportResourceCmd(resource_id, std::move(export_token));
+}
+
+fuchsia::ui::gfx::Command NewImportResourceCmdAsRequest(
+ uint32_t resource_id, fuchsia::ui::gfx::ImportSpec import_spec,
+ zx::eventpair* out_export_token) {
+ ZX_DEBUG_ASSERT(out_export_token);
+ ZX_DEBUG_ASSERT(!*out_export_token);
+
+ zx::eventpair import_token;
+ zx_status_t status =
+ zx::eventpair::create(0u, &import_token, out_export_token);
+ ZX_DEBUG_ASSERT_MSG(status == ZX_OK, "event pair create failed: status=%d",
+ status);
+ return NewImportResourceCmd(resource_id, import_spec,
+ std::move(import_token));
+}
+
+fuchsia::ui::gfx::Command NewSetViewPropertiesCmd(
+ uint32_t view_holder_id, const float bounding_box_min[3],
+ const float bounding_box_max[3], const float inset_from_min[3],
+ const float inset_from_max[3]) {
+ fuchsia::ui::gfx::SetViewPropertiesCmd set_view_properties;
+ set_view_properties.view_holder_id = view_holder_id;
+ auto& props = set_view_properties.properties;
+ props.bounding_box.min = NewVector3(bounding_box_min);
+ props.bounding_box.max = NewVector3(bounding_box_max);
+ props.inset_from_min = NewVector3(inset_from_min);
+ props.inset_from_max = NewVector3(inset_from_max);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_view_properties(std::move(set_view_properties));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetViewPropertiesCmd(
+ uint32_t view_holder_id, const fuchsia::ui::gfx::ViewProperties& props) {
+ fuchsia::ui::gfx::SetViewPropertiesCmd set_view_properties;
+ set_view_properties.view_holder_id = view_holder_id;
+ set_view_properties.properties = props;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_view_properties(std::move(set_view_properties));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewAddChildCmd(uint32_t node_id, uint32_t child_id) {
+ fuchsia::ui::gfx::AddChildCmd add_child;
+ add_child.node_id = node_id;
+ add_child.child_id = child_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_add_child(std::move(add_child));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewAddPartCmd(uint32_t node_id, uint32_t part_id) {
+ fuchsia::ui::gfx::AddPartCmd add_part;
+ add_part.node_id = node_id;
+ add_part.part_id = part_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_add_part(std::move(add_part));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewDetachCmd(uint32_t id) {
+ fuchsia::ui::gfx::DetachCmd detach;
+ detach.id = id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_detach(std::move(detach));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewDetachChildrenCmd(uint32_t node_id) {
+ fuchsia::ui::gfx::DetachChildrenCmd detach_children;
+ detach_children.node_id = node_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_detach_children(std::move(detach_children));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetTranslationCmd(uint32_t node_id,
+ const float translation[3]) {
+ fuchsia::ui::gfx::SetTranslationCmd set_translation;
+ set_translation.id = node_id;
+ set_translation.value = NewVector3Value(translation);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_translation(std::move(set_translation));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetTranslationCmd(uint32_t node_id,
+ uint32_t variable_id) {
+ fuchsia::ui::gfx::SetTranslationCmd set_translation;
+ set_translation.id = node_id;
+ set_translation.value = NewVector3Value(variable_id);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_translation(std::move(set_translation));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetScaleCmd(uint32_t node_id,
+ const float scale[3]) {
+ fuchsia::ui::gfx::SetScaleCmd set_scale;
+ set_scale.id = node_id;
+ set_scale.value = NewVector3Value(scale);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_scale(std::move(set_scale));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetScaleCmd(uint32_t node_id,
+ uint32_t variable_id) {
+ fuchsia::ui::gfx::SetScaleCmd set_scale;
+ set_scale.id = node_id;
+ set_scale.value = NewVector3Value(variable_id);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_scale(std::move(set_scale));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetRotationCmd(uint32_t node_id,
+ const float quaternion[4]) {
+ fuchsia::ui::gfx::SetRotationCmd set_rotation;
+ set_rotation.id = node_id;
+ set_rotation.value = NewQuaternionValue(quaternion);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_rotation(std::move(set_rotation));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetRotationCmd(uint32_t node_id,
+ uint32_t variable_id) {
+ fuchsia::ui::gfx::SetRotationCmd set_rotation;
+ set_rotation.id = node_id;
+ set_rotation.value = NewQuaternionValue(variable_id);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_rotation(std::move(set_rotation));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetAnchorCmd(uint32_t node_id,
+ const float anchor[3]) {
+ fuchsia::ui::gfx::SetAnchorCmd set_anchor;
+ set_anchor.id = node_id;
+ set_anchor.value = NewVector3Value(anchor);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_anchor(std::move(set_anchor));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetAnchorCmd(uint32_t node_id,
+ uint32_t variable_id) {
+ fuchsia::ui::gfx::SetAnchorCmd set_anchor;
+ set_anchor.id = node_id;
+ set_anchor.value = NewVector3Value(variable_id);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_anchor(std::move(set_anchor));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetOpacityCmd(uint32_t node_id, float opacity) {
+ fuchsia::ui::gfx::SetOpacityCmd set_opacity;
+ set_opacity.node_id = node_id;
+ set_opacity.opacity = opacity;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_opacity(std::move(set_opacity));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSendSizeChangeHintCmdHACK(
+ uint32_t node_id, float width_change_factor, float height_change_factor) {
+ fuchsia::ui::gfx::SendSizeChangeHintCmdHACK send_size_change_hint;
+ send_size_change_hint.node_id = node_id;
+ send_size_change_hint.width_change_factor = width_change_factor;
+ send_size_change_hint.height_change_factor = height_change_factor;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_send_size_change_hint_hack(std::move(send_size_change_hint));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetShapeCmd(uint32_t node_id, uint32_t shape_id) {
+ fuchsia::ui::gfx::SetShapeCmd set_shape;
+ set_shape.node_id = node_id;
+ set_shape.shape_id = shape_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_shape(std::move(set_shape));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetMaterialCmd(uint32_t node_id,
+ uint32_t material_id) {
+ fuchsia::ui::gfx::SetMaterialCmd set_material;
+ set_material.node_id = node_id;
+ set_material.material_id = material_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_material(std::move(set_material));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetClipCmd(uint32_t node_id, uint32_t clip_id,
+ bool clip_to_self) {
+ fuchsia::ui::gfx::SetClipCmd set_clip;
+ set_clip.node_id = node_id;
+ set_clip.clip_id = clip_id;
+ set_clip.clip_to_self = clip_to_self;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_clip(std::move(set_clip));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetTagCmd(uint32_t node_id, uint32_t tag_value) {
+ fuchsia::ui::gfx::SetTagCmd set_tag;
+ set_tag.node_id = node_id;
+ set_tag.tag_value = tag_value;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_tag(std::move(set_tag));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewTakeSnapshotCmdHACK(
+ uint32_t node_id, fuchsia::ui::gfx::SnapshotCallbackHACKPtr callback) {
+ fuchsia::ui::gfx::TakeSnapshotCmdHACK snapshot_cmd;
+ snapshot_cmd.node_id = node_id;
+ snapshot_cmd.callback = std::move(callback);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_take_snapshot_cmd(std::move(snapshot_cmd));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetHitTestBehaviorCmd(
+ uint32_t node_id, fuchsia::ui::gfx::HitTestBehavior hit_test_behavior) {
+ fuchsia::ui::gfx::SetHitTestBehaviorCmd set_hit_test_behavior;
+ set_hit_test_behavior.node_id = node_id;
+ set_hit_test_behavior.hit_test_behavior = hit_test_behavior;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_hit_test_behavior(std::move(set_hit_test_behavior));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetCameraCmd(uint32_t renderer_id,
+ uint32_t camera_id) {
+ fuchsia::ui::gfx::SetCameraCmd set_camera;
+ set_camera.renderer_id = renderer_id;
+ set_camera.camera_id = camera_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_camera(std::move(set_camera));
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetTextureCmd(uint32_t material_id,
+ uint32_t texture_id) {
+ fuchsia::ui::gfx::SetTextureCmd set_texture;
+ set_texture.material_id = material_id;
+ set_texture.texture_id = texture_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_texture(std::move(set_texture));
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetColorCmd(uint32_t material_id, uint8_t red,
+ uint8_t green, uint8_t blue,
+ uint8_t alpha) {
+ fuchsia::ui::gfx::ColorRgbaValue color;
+ color.value.red = red;
+ color.value.green = green;
+ color.value.blue = blue;
+ color.value.alpha = alpha;
+ color.variable_id = 0;
+ fuchsia::ui::gfx::SetColorCmd set_color;
+ set_color.material_id = material_id;
+ set_color.color = std::move(color);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_color(std::move(set_color));
+
+ return command;
+}
+
+fuchsia::ui::gfx::MeshVertexFormat NewMeshVertexFormat(
+ fuchsia::ui::gfx::ValueType position_type,
+ fuchsia::ui::gfx::ValueType normal_type,
+ fuchsia::ui::gfx::ValueType tex_coord_type) {
+ fuchsia::ui::gfx::MeshVertexFormat vertex_format;
+ vertex_format.position_type = position_type;
+ vertex_format.normal_type = normal_type;
+ vertex_format.tex_coord_type = tex_coord_type;
+ return vertex_format;
+}
+
+fuchsia::ui::gfx::Command NewBindMeshBuffersCmd(
+ uint32_t mesh_id, uint32_t index_buffer_id,
+ fuchsia::ui::gfx::MeshIndexFormat index_format, uint64_t index_offset,
+ uint32_t index_count, uint32_t vertex_buffer_id,
+ fuchsia::ui::gfx::MeshVertexFormat vertex_format, uint64_t vertex_offset,
+ uint32_t vertex_count, const float bounding_box_min[3],
+ const float bounding_box_max[3]) {
+ fuchsia::ui::gfx::BindMeshBuffersCmd bind_mesh_buffers;
+ bind_mesh_buffers.mesh_id = mesh_id;
+ bind_mesh_buffers.index_buffer_id = index_buffer_id;
+ bind_mesh_buffers.index_format = index_format;
+ bind_mesh_buffers.index_offset = index_offset;
+ bind_mesh_buffers.index_count = index_count;
+ bind_mesh_buffers.vertex_buffer_id = vertex_buffer_id;
+ bind_mesh_buffers.vertex_format = std::move(vertex_format);
+ bind_mesh_buffers.vertex_offset = vertex_offset;
+ bind_mesh_buffers.vertex_count = vertex_count;
+ auto& bbox = bind_mesh_buffers.bounding_box;
+ bbox.min.x = bounding_box_min[0];
+ bbox.min.y = bounding_box_min[1];
+ bbox.min.z = bounding_box_min[2];
+ bbox.max.x = bounding_box_max[0];
+ bbox.max.y = bounding_box_max[1];
+ bbox.max.z = bounding_box_max[2];
+
+ fuchsia::ui::gfx::Command command;
+ command.set_bind_mesh_buffers(std::move(bind_mesh_buffers));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewAddLayerCmd(uint32_t layer_stack_id,
+ uint32_t layer_id) {
+ fuchsia::ui::gfx::AddLayerCmd add_layer;
+ add_layer.layer_stack_id = layer_stack_id;
+ add_layer.layer_id = layer_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_add_layer(std::move(add_layer));
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewRemoveLayerCmd(uint32_t layer_stack_id,
+ uint32_t layer_id) {
+ fuchsia::ui::gfx::RemoveLayerCmd remove_layer;
+ remove_layer.layer_stack_id = layer_stack_id;
+ remove_layer.layer_id = layer_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_remove_layer(std::move(remove_layer));
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewRemoveAllLayersCmd(uint32_t layer_stack_id) {
+ fuchsia::ui::gfx::RemoveAllLayersCmd remove_all_layers;
+ remove_all_layers.layer_stack_id = layer_stack_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_remove_all_layers(std::move(remove_all_layers));
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetLayerStackCmd(uint32_t compositor_id,
+ uint32_t layer_stack_id) {
+ fuchsia::ui::gfx::SetLayerStackCmd set_layer_stack;
+ set_layer_stack.compositor_id = compositor_id;
+ set_layer_stack.layer_stack_id = layer_stack_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_layer_stack(std::move(set_layer_stack));
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetRendererCmd(uint32_t layer_id,
+ uint32_t renderer_id) {
+ fuchsia::ui::gfx::SetRendererCmd set_renderer;
+ set_renderer.layer_id = layer_id;
+ set_renderer.renderer_id = renderer_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_renderer(std::move(set_renderer));
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetRendererParamCmd(
+ uint32_t renderer_id, fuchsia::ui::gfx::RendererParam param) {
+ fuchsia::ui::gfx::SetRendererParamCmd param_command;
+ param_command.renderer_id = renderer_id;
+ param_command.param = std::move(param);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_renderer_param(std::move(param_command));
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetSizeCmd(uint32_t node_id, const float size[2]) {
+ fuchsia::ui::gfx::SetSizeCmd set_size;
+ set_size.id = node_id;
+ auto& value = set_size.value.value;
+ value.x = size[0];
+ value.y = size[1];
+ set_size.value.variable_id = 0;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_size(std::move(set_size));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetCameraTransformCmd(uint32_t camera_id,
+ const float eye_position[3],
+ const float eye_look_at[3],
+ const float eye_up[3]) {
+ fuchsia::ui::gfx::SetCameraTransformCmd set_command;
+ set_command.camera_id = camera_id;
+ set_command.eye_position = NewVector3Value(eye_position);
+ set_command.eye_look_at = NewVector3Value(eye_look_at);
+ set_command.eye_up = NewVector3Value(eye_up);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_camera_transform(std::move(set_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetCameraProjectionCmd(uint32_t camera_id,
+ const float fovy) {
+ fuchsia::ui::gfx::SetCameraProjectionCmd set_command;
+ set_command.camera_id = camera_id;
+ set_command.fovy = NewFloatValue(fovy);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_camera_projection(std::move(set_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetStereoCameraProjectionCmd(
+ uint32_t camera_id, const float left_projection[16],
+ const float right_projection[16]) {
+ fuchsia::ui::gfx::SetStereoCameraProjectionCmd set_command;
+ set_command.camera_id = camera_id;
+ set_command.left_projection = NewMatrix4Value(left_projection);
+ set_command.right_projection = NewMatrix4Value(right_projection);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_stereo_camera_projection(std::move(set_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetCameraPoseBufferCmd(uint32_t camera_id,
+ uint32_t buffer_id,
+ uint32_t num_entries,
+ int64_t base_time,
+ uint64_t time_interval) {
+ fuchsia::ui::gfx::SetCameraPoseBufferCmd set_command;
+ set_command.camera_id = camera_id;
+ set_command.buffer_id = buffer_id;
+ set_command.num_entries = num_entries;
+ set_command.base_time = base_time;
+ set_command.time_interval = time_interval;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_camera_pose_buffer(std::move(set_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetLightColorCmd(uint32_t light_id,
+ const float rgb[3]) {
+ fuchsia::ui::gfx::SetLightColorCmd set_command;
+ set_command.light_id = light_id;
+ set_command.color = NewColorRgbValue(rgb[0], rgb[1], rgb[2]);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_light_color(std::move(set_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetLightColorCmd(uint32_t light_id,
+ uint32_t variable_id) {
+ fuchsia::ui::gfx::SetLightColorCmd set_command;
+ set_command.light_id = light_id;
+ set_command.color = NewColorRgbValue(variable_id);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_light_color(std::move(set_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetLightDirectionCmd(uint32_t light_id,
+ const float dir[3]) {
+ fuchsia::ui::gfx::SetLightDirectionCmd set_command;
+ set_command.light_id = light_id;
+ set_command.direction = NewVector3Value(dir);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_light_direction(std::move(set_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetLightDirectionCmd(uint32_t light_id,
+ uint32_t variable_id) {
+ fuchsia::ui::gfx::SetLightDirectionCmd set_command;
+ set_command.light_id = light_id;
+ set_command.direction = NewVector3Value(variable_id);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_light_direction(std::move(set_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewAddLightCmd(uint32_t scene_id, uint32_t light_id) {
+ fuchsia::ui::gfx::AddLightCmd add_light_command;
+ add_light_command.scene_id = scene_id;
+ add_light_command.light_id = light_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_add_light(std::move(add_light_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewDetachLightCmd(uint32_t light_id) {
+ fuchsia::ui::gfx::DetachLightCmd detach_light_command;
+ detach_light_command.light_id = light_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_detach_light(std::move(detach_light_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewDetachLightsCmd(uint32_t scene_id) {
+ fuchsia::ui::gfx::DetachLightsCmd detach_lights_command;
+ detach_lights_command.scene_id = scene_id;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_detach_lights(std::move(detach_lights_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetEventMaskCmd(uint32_t resource_id,
+ uint32_t event_mask) {
+ fuchsia::ui::gfx::SetEventMaskCmd set_event_mask_command;
+ set_event_mask_command.id = resource_id;
+ set_event_mask_command.event_mask = event_mask;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_event_mask(std::move(set_event_mask_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetLabelCmd(uint32_t resource_id,
+ const std::string& label) {
+ fuchsia::ui::gfx::SetLabelCmd set_label_command;
+ set_label_command.id = resource_id;
+ set_label_command.label = label.substr(0, fuchsia::ui::gfx::kLabelMaxLength);
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_label(std::move(set_label_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::Command NewSetDisableClippingCmd(uint32_t renderer_id,
+ bool disable_clipping) {
+ fuchsia::ui::gfx::SetDisableClippingCmd set_disable_clipping_command;
+ set_disable_clipping_command.renderer_id = renderer_id;
+ set_disable_clipping_command.disable_clipping = disable_clipping;
+
+ fuchsia::ui::gfx::Command command;
+ command.set_set_disable_clipping(std::move(set_disable_clipping_command));
+
+ return command;
+}
+
+fuchsia::ui::gfx::FloatValue NewFloatValue(float value) {
+ fuchsia::ui::gfx::FloatValue val;
+ val.variable_id = 0;
+ val.value = value;
+ return val;
+}
+
+fuchsia::ui::gfx::vec2 NewVector2(const float value[2]) {
+ fuchsia::ui::gfx::vec2 val;
+ val.x = value[0];
+ val.y = value[1];
+ return val;
+}
+
+fuchsia::ui::gfx::Vector2Value NewVector2Value(const float value[2]) {
+ fuchsia::ui::gfx::Vector2Value val;
+ val.variable_id = 0;
+ val.value = NewVector2(value);
+ return val;
+}
+
+fuchsia::ui::gfx::Vector2Value NewVector2Value(uint32_t variable_id) {
+ fuchsia::ui::gfx::Vector2Value val;
+ val.variable_id = variable_id;
+ return val;
+}
+
+fuchsia::ui::gfx::vec3 NewVector3(const float value[3]) {
+ fuchsia::ui::gfx::vec3 val;
+ val.x = value[0];
+ val.y = value[1];
+ val.z = value[2];
+ return val;
+}
+
+fuchsia::ui::gfx::Vector3Value NewVector3Value(const float value[3]) {
+ fuchsia::ui::gfx::Vector3Value val;
+ val.variable_id = 0;
+ val.value = NewVector3(value);
+ return val;
+}
+
+fuchsia::ui::gfx::Vector3Value NewVector3Value(uint32_t variable_id) {
+ fuchsia::ui::gfx::Vector3Value val;
+ val.variable_id = variable_id;
+ return val;
+}
+
+fuchsia::ui::gfx::vec4 NewVector4(const float value[4]) {
+ fuchsia::ui::gfx::vec4 val;
+ val.x = value[0];
+ val.y = value[1];
+ val.z = value[2];
+ val.w = value[3];
+ return val;
+}
+
+fuchsia::ui::gfx::Vector4Value NewVector4Value(const float value[4]) {
+ fuchsia::ui::gfx::Vector4Value val;
+ val.variable_id = 0;
+ val.value = NewVector4(value);
+ return val;
+}
+
+fuchsia::ui::gfx::Vector4Value NewVector4Value(uint32_t variable_id) {
+ fuchsia::ui::gfx::Vector4Value val;
+ val.variable_id = variable_id;
+ return val;
+}
+
+fuchsia::ui::gfx::Quaternion NewQuaternion(const float value[4]) {
+ fuchsia::ui::gfx::Quaternion val;
+ val.x = value[0];
+ val.y = value[1];
+ val.z = value[2];
+ val.w = value[3];
+ return val;
+}
+
+fuchsia::ui::gfx::QuaternionValue NewQuaternionValue(const float value[4]) {
+ fuchsia::ui::gfx::QuaternionValue val;
+ val.variable_id = 0;
+ val.value = NewQuaternion(value);
+ return val;
+}
+
+fuchsia::ui::gfx::QuaternionValue NewQuaternionValue(uint32_t variable_id) {
+ fuchsia::ui::gfx::QuaternionValue val;
+ val.variable_id = variable_id;
+ return val;
+}
+
+fuchsia::ui::gfx::mat4 NewMatrix4(const float matrix[16]) {
+ fuchsia::ui::gfx::mat4 val;
+ val.matrix[0] = matrix[0];
+ val.matrix[1] = matrix[1];
+ val.matrix[2] = matrix[2];
+ val.matrix[3] = matrix[3];
+ val.matrix[4] = matrix[4];
+ val.matrix[5] = matrix[5];
+ val.matrix[6] = matrix[6];
+ val.matrix[7] = matrix[7];
+ val.matrix[8] = matrix[8];
+ val.matrix[9] = matrix[9];
+ val.matrix[10] = matrix[10];
+ val.matrix[11] = matrix[11];
+ val.matrix[12] = matrix[12];
+ val.matrix[13] = matrix[13];
+ val.matrix[14] = matrix[14];
+ val.matrix[15] = matrix[15];
+ return val;
+}
+
+fuchsia::ui::gfx::Matrix4Value NewMatrix4Value(const float matrix[16]) {
+ fuchsia::ui::gfx::Matrix4Value val;
+ val.variable_id = 0;
+ val.value = NewMatrix4(matrix);
+ return val;
+}
+
+fuchsia::ui::gfx::Matrix4Value NewMatrix4Value(uint32_t variable_id) {
+ fuchsia::ui::gfx::Matrix4Value val;
+ val.variable_id = variable_id;
+ return val;
+}
+
+fuchsia::ui::gfx::ColorRgbValue NewColorRgbValue(float red, float green,
+ float blue) {
+ fuchsia::ui::gfx::ColorRgbValue val;
+ val.variable_id = 0;
+ auto& color = val.value;
+ color.red = red;
+ color.green = green;
+ color.blue = blue;
+
+ return val;
+}
+
+fuchsia::ui::gfx::ColorRgbValue NewColorRgbValue(uint32_t variable_id) {
+ fuchsia::ui::gfx::ColorRgbValue val;
+ val.variable_id = variable_id;
+
+ return val;
+}
+
+fuchsia::ui::gfx::ColorRgbaValue NewColorRgbaValue(const uint8_t value[4]) {
+ fuchsia::ui::gfx::ColorRgbaValue val;
+ val.variable_id = 0;
+ auto& color = val.value;
+ color.red = value[0];
+ color.green = value[1];
+ color.blue = value[2];
+ color.alpha = value[3];
+
+ return val;
+}
+
+fuchsia::ui::gfx::ColorRgbaValue NewColorRgbaValue(uint32_t variable_id) {
+ fuchsia::ui::gfx::ColorRgbaValue val;
+ val.variable_id = variable_id;
+
+ return val;
+}
+
+// TODO(mikejurka): this should be in an images util file
+bool ImageInfoEquals(const fuchsia::images::ImageInfo& a,
+ const fuchsia::images::ImageInfo& b) {
+ return a.transform == b.transform && a.width == b.width &&
+ a.height == b.height && a.stride == b.stride &&
+ a.pixel_format == b.pixel_format && a.color_space == b.color_space &&
+ a.tiling == b.tiling && a.alpha_format == b.alpha_format;
+}
+
+} // namespace scenic
diff --git a/pkg/scenic_cpp/host_image_cycler.cc b/pkg/scenic_cpp/host_image_cycler.cc
new file mode 100644
index 0000000..70b45d6
--- /dev/null
+++ b/pkg/scenic_cpp/host_image_cycler.cc
@@ -0,0 +1,65 @@
+// Copyright 2017 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/ui/scenic/cpp/host_image_cycler.h"
+
+#include <zircon/assert.h>
+
+namespace scenic {
+
+HostImageCycler::HostImageCycler(Session* session)
+ : EntityNode(session),
+ content_node_(session),
+ content_material_(session),
+ image_pool_(session, kNumBuffers) {
+ content_node_.SetMaterial(content_material_);
+ AddChild(content_node_);
+}
+
+HostImageCycler::~HostImageCycler() = default;
+
+const HostImage* HostImageCycler::AcquireImage(
+ uint32_t width, uint32_t height, uint32_t stride,
+ fuchsia::images::PixelFormat pixel_format,
+ fuchsia::images::ColorSpace color_space) {
+ ZX_DEBUG_ASSERT(!acquired_image_);
+
+ // Update the image pool and content shape.
+ fuchsia::images::ImageInfo image_info;
+ image_info.width = width;
+ image_info.height = height;
+ image_info.stride = stride;
+ image_info.pixel_format = pixel_format;
+ image_info.color_space = color_space;
+ image_info.tiling = fuchsia::images::Tiling::LINEAR;
+ reconfigured_ = image_pool_.Configure(&image_info);
+
+ const HostImage* image = image_pool_.GetImage(image_index_);
+ ZX_DEBUG_ASSERT(image);
+ acquired_image_ = true;
+ return image;
+}
+
+void HostImageCycler::ReleaseAndSwapImage() {
+ ZX_DEBUG_ASSERT(acquired_image_);
+ acquired_image_ = false;
+
+ const HostImage* image = image_pool_.GetImage(image_index_);
+ ZX_DEBUG_ASSERT(image);
+ content_material_.SetTexture(*image);
+
+ if (reconfigured_) {
+ Rectangle content_rect(content_node_.session(),
+ image_pool_.image_info()->width,
+ image_pool_.image_info()->height);
+ content_node_.SetShape(content_rect);
+ reconfigured_ = false;
+ }
+
+ // TODO(MZ-145): Define an |InvalidateCmd| on |Image| instead.
+ image_pool_.DiscardImage(image_index_);
+ image_index_ = (image_index_ + 1) % kNumBuffers;
+}
+
+} // namespace scenic
diff --git a/pkg/scenic_cpp/host_memory.cc b/pkg/scenic_cpp/host_memory.cc
new file mode 100644
index 0000000..d01e665
--- /dev/null
+++ b/pkg/scenic_cpp/host_memory.cc
@@ -0,0 +1,151 @@
+// Copyright 2017 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/ui/scenic/cpp/host_memory.h"
+
+#include <lib/zx/vmar.h>
+#include <lib/zx/vmo.h>
+#include <zircon/assert.h>
+#include <memory>
+
+#include "lib/ui/scenic/cpp/commands.h"
+
+namespace scenic {
+namespace {
+
+// Returns true if a memory object is of an appropriate size to recycle.
+bool CanReuseMemory(const HostMemory& memory, size_t desired_size) {
+ return memory.data_size() >= desired_size &&
+ memory.data_size() <= desired_size * 2;
+}
+
+std::pair<zx::vmo, std::shared_ptr<HostData>> AllocateMemory(size_t size) {
+ // Create the vmo and map it into this process.
+ zx::vmo local_vmo;
+ zx_status_t status = zx::vmo::create(size, 0u, &local_vmo);
+ ZX_ASSERT_MSG(status == ZX_OK, "vmo create failed: status=%d", status);
+ auto data = std::make_shared<HostData>(local_vmo, 0u, size);
+
+ // Drop rights before we transfer the VMO to the session manager.
+ // TODO(MA-492): Now that host-local memory may be concurrently used as
+ // device-local memory on UMA platforms, we need to keep all permissions on
+ // the duplicated vmo handle, until Vulkan can import read-only memory.
+ zx::vmo remote_vmo;
+ status = local_vmo.replace(ZX_RIGHT_SAME_RIGHTS, &remote_vmo);
+ ZX_ASSERT_MSG(status == ZX_OK, "replace rights failed: status=%d", status);
+ return std::make_pair(std::move(remote_vmo), std::move(data));
+}
+
+} // namespace
+
+HostData::HostData(const zx::vmo& vmo, off_t offset, size_t size)
+ : size_(size) {
+ static const uint32_t flags =
+ ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE | ZX_VM_FLAG_MAP_RANGE;
+ uintptr_t ptr;
+ zx_status_t status =
+ zx::vmar::root_self()->map(0, vmo, offset, size, flags, &ptr);
+ ZX_ASSERT_MSG(status == ZX_OK, "map failed: status=%d", status);
+ ptr_ = reinterpret_cast<void*>(ptr);
+}
+
+HostData::~HostData() {
+ zx_status_t status =
+ zx::vmar::root_self()->unmap(reinterpret_cast<uintptr_t>(ptr_), size_);
+ ZX_ASSERT_MSG(status == ZX_OK, "unmap failed: status=%d", status);
+}
+
+HostMemory::HostMemory(Session* session, size_t size)
+ : HostMemory(session, AllocateMemory(size)) {}
+
+HostMemory::HostMemory(Session* session,
+ std::pair<zx::vmo, std::shared_ptr<HostData>> init)
+ : Memory(session, std::move(init.first), init.second->size(),
+ fuchsia::images::MemoryType::HOST_MEMORY),
+ data_(std::move(init.second)) {}
+
+HostMemory::HostMemory(HostMemory&& moved)
+ : Memory(std::move(moved)), data_(std::move(moved.data_)) {}
+
+HostMemory::~HostMemory() = default;
+
+HostImage::HostImage(const HostMemory& memory, off_t memory_offset,
+ fuchsia::images::ImageInfo info)
+ : HostImage(memory.session(), memory.id(), memory_offset, memory.data(),
+ std::move(info)) {}
+
+HostImage::HostImage(Session* session, uint32_t memory_id, off_t memory_offset,
+ std::shared_ptr<HostData> data,
+ fuchsia::images::ImageInfo info)
+ : Image(session, memory_id, memory_offset, std::move(info)),
+ data_(std::move(data)) {}
+
+HostImage::HostImage(HostImage&& moved)
+ : Image(std::move(moved)), data_(std::move(moved.data_)) {}
+
+HostImage::~HostImage() = default;
+
+HostImagePool::HostImagePool(Session* session, uint32_t num_images)
+ : session_(session), image_ptrs_(num_images), memory_ptrs_(num_images) {}
+
+HostImagePool::~HostImagePool() = default;
+
+// TODO(mikejurka): Double-check these changes
+bool HostImagePool::Configure(const fuchsia::images::ImageInfo* image_info) {
+ if (image_info) {
+ if (configured_ && ImageInfoEquals(*image_info, image_info_)) {
+ return false; // no change
+ }
+ configured_ = true;
+ image_info_ = *image_info;
+ } else {
+ if (!configured_) {
+ return false; // no change
+ }
+ configured_ = false;
+ }
+
+ for (uint32_t i = 0; i < num_images(); i++)
+ image_ptrs_[i].reset();
+
+ if (configured_) {
+ ZX_DEBUG_ASSERT(image_info_.width > 0);
+ ZX_DEBUG_ASSERT(image_info_.height > 0);
+ ZX_DEBUG_ASSERT(image_info_.stride > 0);
+
+ size_t desired_size = Image::ComputeSize(image_info_);
+ for (uint32_t i = 0; i < num_images(); i++) {
+ if (memory_ptrs_[i] && !CanReuseMemory(*memory_ptrs_[i], desired_size))
+ memory_ptrs_[i].reset();
+ }
+ }
+ return true;
+}
+
+const HostImage* HostImagePool::GetImage(uint32_t index) {
+ ZX_DEBUG_ASSERT(index < num_images());
+
+ if (image_ptrs_[index])
+ return image_ptrs_[index].get();
+
+ if (!configured_)
+ return nullptr;
+
+ if (!memory_ptrs_[index]) {
+ memory_ptrs_[index] =
+ std::make_unique<HostMemory>(session_, Image::ComputeSize(image_info_));
+ }
+
+ image_ptrs_[index] =
+ std::make_unique<HostImage>(*memory_ptrs_[index], 0u, image_info_);
+ return image_ptrs_[index].get();
+}
+
+void HostImagePool::DiscardImage(uint32_t index) {
+ ZX_DEBUG_ASSERT(index < num_images());
+
+ image_ptrs_[index].reset();
+}
+
+} // namespace scenic
diff --git a/pkg/scenic_cpp/include/lib/ui/scenic/cpp/commands.h b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/commands.h
new file mode 100644
index 0000000..2c44a2f
--- /dev/null
+++ b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/commands.h
@@ -0,0 +1,268 @@
+// Copyright 2017 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.
+
+#ifndef LIB_UI_SCENIC_CPP_COMMANDS_H_
+#define LIB_UI_SCENIC_CPP_COMMANDS_H_
+
+#include <string>
+
+#include <fuchsia/images/cpp/fidl.h>
+#include <fuchsia/ui/gfx/cpp/fidl.h>
+#include <fuchsia/ui/scenic/cpp/fidl.h>
+
+namespace scenic {
+
+constexpr float kZeroesFloat3[3] = {0.f, 0.f, 0.f};
+constexpr float kOnesFloat3[3] = {1.f, 1.f, 1.f};
+// A quaterion that has no rotation.
+constexpr float kQuaternionDefault[4] = {0.f, 0.f, 0.f, 1.f};
+
+// Helper function for wrapping a GFX command as a Scenic command.
+fuchsia::ui::scenic::Command NewCommand(fuchsia::ui::gfx::Command command);
+
+// Helper function for wrapping an input command as a Scenic command.
+fuchsia::ui::scenic::Command NewCommand(fuchsia::ui::input::Command command);
+
+// Resource creation.
+fuchsia::ui::gfx::Command NewCreateMemoryCmd(
+ uint32_t id, zx::vmo vmo, uint64_t allocation_size,
+ fuchsia::images::MemoryType memory_type);
+fuchsia::ui::gfx::Command NewCreateImageCmd(uint32_t id, uint32_t memory_id,
+ uint32_t memory_offset,
+ fuchsia::images::ImageInfo info);
+fuchsia::ui::gfx::Command NewCreateImageCmd(
+ uint32_t id, uint32_t memory_id, uint32_t memory_offset,
+ fuchsia::images::PixelFormat format,
+ fuchsia::images::ColorSpace color_space, fuchsia::images::Tiling tiling,
+ uint32_t width, uint32_t height, uint32_t stride);
+fuchsia::ui::gfx::Command NewCreateImagePipeCmd(
+ uint32_t id, ::fidl::InterfaceRequest<fuchsia::images::ImagePipe> request);
+fuchsia::ui::gfx::Command NewCreateBufferCmd(uint32_t id, uint32_t memory_id,
+ uint32_t memory_offset,
+ uint32_t num_bytes);
+
+fuchsia::ui::gfx::Command NewCreateCompositorCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateDisplayCompositorCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateLayerStackCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateLayerCmd(uint32_t id);
+
+fuchsia::ui::gfx::Command NewCreateSceneCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateCameraCmd(uint32_t id, uint32_t scene_id);
+fuchsia::ui::gfx::Command NewCreateStereoCameraCmd(uint32_t id,
+ uint32_t scene_id);
+fuchsia::ui::gfx::Command NewCreateRendererCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateAmbientLightCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateDirectionalLightCmd(uint32_t id);
+
+fuchsia::ui::gfx::Command NewCreateCircleCmd(uint32_t id, float radius);
+fuchsia::ui::gfx::Command NewCreateRectangleCmd(uint32_t id, float width,
+ float height);
+fuchsia::ui::gfx::Command NewCreateRoundedRectangleCmd(
+ uint32_t id, float width, float height, float top_left_radius,
+ float top_right_radius, float bottom_right_radius,
+ float bottom_left_radius);
+
+// Variant of NewCreateCircleCmd that uses a variable radius instead of a
+// constant one set at construction time.
+fuchsia::ui::gfx::Command NewCreateVarCircleCmd(uint32_t id,
+ uint32_t radius_var_id);
+// Variant of NewCreateRectangleCmd that uses a variable width/height
+// instead of constant ones set at construction time.
+fuchsia::ui::gfx::Command NewCreateVarRectangleCmd(uint32_t id,
+ uint32_t width_var_id,
+ uint32_t height_var_id);
+// Variant of NewCreateRoundedRectangleCmd that uses a variable
+// width/height/etc. instead of constant ones set at construction time.
+fuchsia::ui::gfx::Command NewCreateVarRoundedRectangleCmd(
+ uint32_t id, uint32_t width_var_id, uint32_t height_var_id,
+ uint32_t top_left_radius_var_id, uint32_t top_right_radius_var_id,
+ uint32_t bottom_left_radius_var_id, uint32_t bottom_right_radius_var_id);
+
+fuchsia::ui::gfx::Command NewCreateMeshCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateMaterialCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateClipNodeCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateEntityNodeCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateOpacityNodeCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateShapeNodeCmd(uint32_t id);
+fuchsia::ui::gfx::Command NewCreateViewCmd(uint32_t id, zx::eventpair token,
+ const std::string& debug_name);
+fuchsia::ui::gfx::Command NewCreateViewHolderCmd(uint32_t id,
+ zx::eventpair token,
+ const std::string& debug_name);
+fuchsia::ui::gfx::Command NewCreateVariableCmd(uint32_t id,
+ fuchsia::ui::gfx::Value value);
+
+fuchsia::ui::gfx::Command NewReleaseResourceCmd(uint32_t id);
+
+// Export & Import operations.
+fuchsia::ui::gfx::Command NewExportResourceCmd(uint32_t resource_id,
+ zx::eventpair export_token);
+fuchsia::ui::gfx::Command NewImportResourceCmd(
+ uint32_t resource_id, fuchsia::ui::gfx::ImportSpec spec,
+ zx::eventpair import_token);
+
+// Exports the resource and returns an import token in |out_import_token|
+// which allows it to be imported into other sessions.
+fuchsia::ui::gfx::Command NewExportResourceCmdAsRequest(
+ uint32_t resource_id, zx::eventpair* out_import_token);
+
+// Imports the resource and returns an export token in |out_export_token|
+// by which another session can export a resource to associate with this import.
+fuchsia::ui::gfx::Command NewImportResourceCmdAsRequest(
+ uint32_t resource_id, fuchsia::ui::gfx::ImportSpec import_spec,
+ zx::eventpair* out_export_token);
+
+// View/ViewHolder commands.
+fuchsia::ui::gfx::Command NewSetViewPropertiesCmd(
+ uint32_t view_holder_id, const float bounding_box_min[3],
+ const float bounding_box_max[3], const float inset_from_min[3],
+ const float inset_from_max[3]);
+fuchsia::ui::gfx::Command NewSetViewPropertiesCmd(
+ uint32_t view_holder_id, const fuchsia::ui::gfx::ViewProperties& props);
+
+// Node operations.
+fuchsia::ui::gfx::Command NewAddChildCmd(uint32_t node_id, uint32_t child_id);
+fuchsia::ui::gfx::Command NewAddPartCmd(uint32_t node_id, uint32_t part_id);
+fuchsia::ui::gfx::Command NewDetachCmd(uint32_t node_id);
+fuchsia::ui::gfx::Command NewDetachChildrenCmd(uint32_t node_id);
+fuchsia::ui::gfx::Command NewSetTranslationCmd(uint32_t node_id,
+ const float translation[3]);
+fuchsia::ui::gfx::Command NewSetTranslationCmd(uint32_t node_id,
+ uint32_t variable_id);
+fuchsia::ui::gfx::Command NewSetScaleCmd(uint32_t node_id,
+ const float scale[3]);
+fuchsia::ui::gfx::Command NewSetScaleCmd(uint32_t node_id,
+ uint32_t variable_id);
+fuchsia::ui::gfx::Command NewSetRotationCmd(uint32_t node_id,
+ const float quaternion[4]);
+fuchsia::ui::gfx::Command NewSetRotationCmd(uint32_t node_id,
+ uint32_t variable_id);
+fuchsia::ui::gfx::Command NewSetAnchorCmd(uint32_t node_id,
+ const float anchor[3]);
+fuchsia::ui::gfx::Command NewSetAnchorCmd(uint32_t node_id,
+ uint32_t variable_id);
+
+fuchsia::ui::gfx::Command NewSetOpacityCmd(uint32_t node_id, float opacity);
+fuchsia::ui::gfx::Command NewSendSizeChangeHintCmdHACK(
+ uint32_t node_id, float width_change_factor, float height_change_factor);
+fuchsia::ui::gfx::Command NewSetShapeCmd(uint32_t node_id, uint32_t shape_id);
+fuchsia::ui::gfx::Command NewSetMaterialCmd(uint32_t node_id,
+ uint32_t material_id);
+fuchsia::ui::gfx::Command NewSetClipCmd(uint32_t node_id, uint32_t clip_id,
+ bool clip_to_self);
+fuchsia::ui::gfx::Command NewSetTagCmd(uint32_t node_id, uint32_t tag_value);
+fuchsia::ui::gfx::Command NewSetHitTestBehaviorCmd(
+ uint32_t node_id, fuchsia::ui::gfx::HitTestBehavior hit_test_behavior);
+fuchsia::ui::gfx::Command NewTakeSnapshotCmdHACK(
+ uint32_t id, fuchsia::ui::gfx::SnapshotCallbackHACKPtr callback);
+
+// Camera and lighting operations.
+
+fuchsia::ui::gfx::Command NewSetCameraCmd(uint32_t renderer_id,
+ uint32_t camera_id);
+fuchsia::ui::gfx::Command NewSetCameraTransformCmd(uint32_t camera_id,
+ const float eye_position[3],
+ const float eye_look_at[3],
+ const float eye_up[3]);
+fuchsia::ui::gfx::Command NewSetCameraProjectionCmd(uint32_t camera_id,
+ const float fovy);
+
+fuchsia::ui::gfx::Command NewSetCameraPoseBufferCmd(uint32_t camera_id,
+ uint32_t buffer_id,
+ uint32_t num_entries,
+ int64_t base_time,
+ uint64_t time_interval);
+
+fuchsia::ui::gfx::Command NewSetStereoCameraProjectionCmd(
+ uint32_t camera_id, const float left_projection[16],
+ const float right_projection[16]);
+
+fuchsia::ui::gfx::Command NewSetLightColorCmd(uint32_t light_id,
+ const float rgb[3]);
+fuchsia::ui::gfx::Command NewSetLightColorCmd(uint32_t light_id,
+ uint32_t variable_id);
+fuchsia::ui::gfx::Command NewSetLightDirectionCmd(uint32_t light_id,
+ const float direction[3]);
+fuchsia::ui::gfx::Command NewSetLightDirectionCmd(uint32_t light_id,
+ uint32_t variable_id);
+fuchsia::ui::gfx::Command NewAddLightCmd(uint32_t scene_id, uint32_t light_id);
+fuchsia::ui::gfx::Command NewDetachLightCmd(uint32_t light_id);
+fuchsia::ui::gfx::Command NewDetachLightsCmd(uint32_t scene_id);
+
+// Material operations.
+fuchsia::ui::gfx::Command NewSetTextureCmd(uint32_t node_id, uint32_t image_id);
+fuchsia::ui::gfx::Command NewSetColorCmd(uint32_t node_id, uint8_t red,
+ uint8_t green, uint8_t blue,
+ uint8_t alpha);
+
+// Mesh operations.
+fuchsia::ui::gfx::MeshVertexFormat NewMeshVertexFormat(
+ fuchsia::ui::gfx::ValueType position_type,
+ fuchsia::ui::gfx::ValueType normal_type,
+ fuchsia::ui::gfx::ValueType tex_coord_type);
+// These arguments are documented in commands.fidl; see BindMeshBuffersCmd.
+fuchsia::ui::gfx::Command NewBindMeshBuffersCmd(
+ uint32_t mesh_id, uint32_t index_buffer_id,
+ fuchsia::ui::gfx::MeshIndexFormat index_format, uint64_t index_offset,
+ uint32_t index_count, uint32_t vertex_buffer_id,
+ fuchsia::ui::gfx::MeshVertexFormat vertex_format, uint64_t vertex_offset,
+ uint32_t vertex_count, const float bounding_box_min[3],
+ const float bounding_box_max[3]);
+
+// Layer / LayerStack / Compositor operations.
+fuchsia::ui::gfx::Command NewAddLayerCmd(uint32_t layer_stack_id,
+ uint32_t layer_id);
+fuchsia::ui::gfx::Command NewRemoveLayerCmd(uint32_t layer_stack_id,
+ uint32_t layer_id);
+fuchsia::ui::gfx::Command NewRemoveAllLayersCmd(uint32_t layer_stack_id);
+fuchsia::ui::gfx::Command NewSetLayerStackCmd(uint32_t compositor_id,
+ uint32_t layer_stack_id);
+fuchsia::ui::gfx::Command NewSetRendererCmd(uint32_t layer_id,
+ uint32_t renderer_id);
+fuchsia::ui::gfx::Command NewSetRendererParamCmd(
+ uint32_t renderer_id, fuchsia::ui::gfx::RendererParam param);
+fuchsia::ui::gfx::Command NewSetSizeCmd(uint32_t node_id, const float size[2]);
+
+// Event operations.
+fuchsia::ui::gfx::Command NewSetEventMaskCmd(uint32_t resource_id,
+ uint32_t event_mask);
+
+// Diagnostic operations.
+fuchsia::ui::gfx::Command NewSetLabelCmd(uint32_t resource_id,
+ const std::string& label);
+
+// Debugging operations.
+fuchsia::ui::gfx::Command NewSetDisableClippingCmd(uint32_t resource_id,
+ bool disable_clipping);
+
+// Basic types.
+fuchsia::ui::gfx::FloatValue NewFloatValue(float value);
+fuchsia::ui::gfx::Vector2Value NewVector2Value(const float value[2]);
+fuchsia::ui::gfx::Vector2Value NewVector2Value(uint32_t variable_id);
+fuchsia::ui::gfx::Vector3Value NewVector3Value(const float value[3]);
+fuchsia::ui::gfx::Vector3Value NewVector3Value(uint32_t variable_id);
+fuchsia::ui::gfx::Vector4Value NewVector4Value(const float value[4]);
+fuchsia::ui::gfx::Vector4Value NewVector4Value(uint32_t variable_id);
+fuchsia::ui::gfx::QuaternionValue NewQuaternionValue(const float value[4]);
+fuchsia::ui::gfx::QuaternionValue NewQuaternionValue(uint32_t variable_id);
+fuchsia::ui::gfx::Matrix4Value NewMatrix4Value(const float value[16]);
+fuchsia::ui::gfx::Matrix4Value NewMatrix4Value(uint32_t variable_id);
+fuchsia::ui::gfx::ColorRgbValue NewColorRgbValue(float red, float green,
+ float blue);
+fuchsia::ui::gfx::ColorRgbValue NewColorRgbValue(uint32_t variable_id);
+fuchsia::ui::gfx::ColorRgbaValue NewColorRgbaValue(const uint8_t value[4]);
+fuchsia::ui::gfx::ColorRgbaValue NewColorRgbaValue(uint32_t variable_id);
+fuchsia::ui::gfx::QuaternionValue NewQuaternionValue(const float value[4]);
+fuchsia::ui::gfx::vec2 NewVector2(const float value[2]);
+fuchsia::ui::gfx::vec3 NewVector3(const float value[3]);
+fuchsia::ui::gfx::vec4 NewVector4(const float value[4]);
+
+// Utilities.
+
+bool ImageInfoEquals(const fuchsia::images::ImageInfo& a,
+ const fuchsia::images::ImageInfo& b);
+
+} // namespace scenic
+
+#endif // LIB_UI_SCENIC_CPP_COMMANDS_H_
diff --git a/pkg/scenic_cpp/include/lib/ui/scenic/cpp/host_image_cycler.h b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/host_image_cycler.h
new file mode 100644
index 0000000..0e89ce4
--- /dev/null
+++ b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/host_image_cycler.h
@@ -0,0 +1,50 @@
+// Copyright 2017 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.
+
+#ifndef LIB_UI_SCENIC_CPP_HOST_IMAGE_CYCLER_H_
+#define LIB_UI_SCENIC_CPP_HOST_IMAGE_CYCLER_H_
+
+#include "lib/ui/scenic/cpp/host_memory.h"
+
+namespace scenic {
+
+// Creates a node which presents double-buffered content drawn to an image
+// in host memory.
+class HostImageCycler : public scenic::EntityNode {
+ public:
+ explicit HostImageCycler(scenic::Session* session);
+ ~HostImageCycler();
+
+ HostImageCycler(const HostImageCycler&) = delete;
+ HostImageCycler& operator=(const HostImageCycler&) = delete;
+
+ // Acquires an image for rendering.
+ // At most one image can be acquired at a time.
+ // The client is responsible for clearing the image.
+ const HostImage* AcquireImage(uint32_t width, uint32_t height,
+ uint32_t stride,
+ fuchsia::images::PixelFormat pixel_format =
+ fuchsia::images::PixelFormat::BGRA_8,
+ fuchsia::images::ColorSpace color_space =
+ fuchsia::images::ColorSpace::SRGB);
+
+ // Releases the image most recently acquired using |AcquireImage()|.
+ // Sets the content node's texture to be backed by the image.
+ void ReleaseAndSwapImage();
+
+ private:
+ static constexpr uint32_t kNumBuffers = 2u;
+
+ scenic::ShapeNode content_node_;
+ scenic::Material content_material_;
+ scenic::HostImagePool image_pool_;
+
+ bool acquired_image_ = false;
+ bool reconfigured_ = false;
+ uint32_t image_index_ = 0u;
+};
+
+} // namespace scenic
+
+#endif // LIB_UI_SCENIC_CPP_HOST_IMAGE_CYCLER_H_
diff --git a/pkg/scenic_cpp/include/lib/ui/scenic/cpp/host_memory.h b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/host_memory.h
new file mode 100644
index 0000000..6209422
--- /dev/null
+++ b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/host_memory.h
@@ -0,0 +1,149 @@
+// Copyright 2017 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.
+
+#ifndef LIB_UI_SCENIC_CPP_HOST_MEMORY_H_
+#define LIB_UI_SCENIC_CPP_HOST_MEMORY_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <lib/zx/vmo.h>
+
+#include "lib/ui/scenic/cpp/resources.h"
+
+namespace scenic {
+
+// Provides access to data stored in a host-accessible shared memory region.
+// The memory is unmapped once all references to this object have been released.
+class HostData : public std::enable_shared_from_this<HostData> {
+ public:
+ // Maps a range of an existing VMO into memory.
+ HostData(const zx::vmo& vmo, off_t offset, size_t size);
+ ~HostData();
+
+ HostData(const HostData&) = delete;
+ HostData& operator=(const HostData&) = delete;
+
+ // Gets the size of the data in bytes.
+ size_t size() const { return size_; }
+
+ // Gets a pointer to the data.
+ void* ptr() const { return ptr_; }
+
+ private:
+ size_t const size_;
+ void* ptr_;
+};
+
+// Represents a host-accessible shared memory backed memory resource in a
+// session. The memory is mapped read/write into this process and transferred
+// read-only to the scene manager. The shared memory region is retained until
+// this object is destroyed.
+// TODO(MZ-268): Don't inherit from Memory, so that Memory can have a public
+// move constructor.
+// TODO(MA-492): The memory is currently not transferred read-only, as we may
+// choose to map it as device-local memory on UMA platforms, and Vulkan requires
+// a read/write vmo in order to successfully import the memory.
+class HostMemory final : public Memory {
+ public:
+ HostMemory(Session* session, size_t size);
+ HostMemory(HostMemory&& moved);
+ ~HostMemory();
+
+ HostMemory(const HostMemory&) = delete;
+ HostMemory& operator=(const HostMemory&) = delete;
+
+ // Gets a reference to the underlying shared memory region.
+ const std::shared_ptr<HostData>& data() const { return data_; }
+
+ // Gets the size of the data in bytes.
+ size_t data_size() const { return data_->size(); }
+
+ // Gets a pointer to the data.
+ void* data_ptr() const { return data_->ptr(); }
+
+ private:
+ explicit HostMemory(Session* session,
+ std::pair<zx::vmo, std::shared_ptr<HostData>> init);
+
+ std::shared_ptr<HostData> data_;
+};
+
+// Represents an image resource backed by host-accessible shared memory bound to
+// a session. The shared memory region is retained until this object is
+// destroyed.
+// TODO(MZ-268): Don't inherit from Image, so that Image can have a public move
+// constructor.
+class HostImage final : public Image {
+ public:
+ HostImage(const HostMemory& memory, off_t memory_offset,
+ fuchsia::images::ImageInfo info);
+ HostImage(Session* session, uint32_t memory_id, off_t memory_offset,
+ std::shared_ptr<HostData> data, fuchsia::images::ImageInfo info);
+ HostImage(HostImage&& moved);
+ ~HostImage();
+
+ HostImage(const HostImage&) = delete;
+ HostImage& operator=(const HostImage&) = delete;
+
+ // Gets a reference to the underlying shared memory region.
+ const std::shared_ptr<HostData>& data() const { return data_; }
+
+ // Gets a pointer to the image data.
+ void* image_ptr() const {
+ return static_cast<uint8_t*>(data_->ptr()) + memory_offset();
+ }
+
+ private:
+ std::shared_ptr<HostData> data_;
+};
+
+// Represents a pool of image resources backed by host-accessible shared memory
+// bound to a session. All images in the pool must have the same layout.
+class HostImagePool {
+ public:
+ // Creates a pool which can supply up to |num_images| images on demand.
+ explicit HostImagePool(Session* session, uint32_t num_images);
+ ~HostImagePool();
+
+ HostImagePool(const HostImagePool&) = delete;
+ HostImagePool& operator=(const HostImagePool&) = delete;
+
+ // The number of images which this pool can manage.
+ uint32_t num_images() const { return image_ptrs_.size(); }
+
+ // Gets information about the images in the pool, or nullptr if the
+ // pool is not configured.
+ const fuchsia::images::ImageInfo* image_info() const { return &image_info_; }
+
+ // Sets the image information for images in the pool.
+ // Previously created images are released but their memory may be reused.
+ // If |image_info| is nullptr, the pool reverts to an non-configured state;
+ // all images are released but the memory is retained for recycling.
+ // Returns true if the configuration changed.
+ bool Configure(const fuchsia::images::ImageInfo* image_info);
+
+ // Gets the image with the specified index.
+ // The |index| must be between 0 and |num_images() - 1|.
+ // The returned pointer is valid until the image is discarded or the
+ // pool is reconfigured. Returns nullptr if the pool is not configured.
+ const HostImage* GetImage(uint32_t index);
+
+ // Discards the image with the specified index but recycles its memory.
+ // The |index| must be between 0 and |num_images() - 1|.
+ void DiscardImage(uint32_t index);
+
+ private:
+ Session* const session_;
+
+ bool configured_ = false;
+ fuchsia::images::ImageInfo image_info_;
+ std::vector<std::unique_ptr<HostImage>> image_ptrs_;
+ std::vector<std::unique_ptr<HostMemory>> memory_ptrs_;
+};
+
+} // namespace scenic
+
+#endif // LIB_UI_SCENIC_CPP_HOST_MEMORY_H_
diff --git a/pkg/scenic_cpp/include/lib/ui/scenic/cpp/id.h b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/id.h
new file mode 100644
index 0000000..a500046
--- /dev/null
+++ b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/id.h
@@ -0,0 +1,17 @@
+// 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.
+
+#ifndef LIB_UI_SCENIC_CPP_ID_H_
+#define LIB_UI_SCENIC_CPP_ID_H_
+
+#include <cstdint>
+
+namespace scenic {
+
+using SessionId = uint64_t;
+using ResourceId = uint32_t;
+
+} // namespace scenic
+
+#endif // LIB_UI_SCENIC_CPP_ID_H_
diff --git a/pkg/scenic_cpp/include/lib/ui/scenic/cpp/resources.h b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/resources.h
new file mode 100644
index 0000000..40d6b0c
--- /dev/null
+++ b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/resources.h
@@ -0,0 +1,593 @@
+// Copyright 2017 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.
+
+#ifndef LIB_UI_SCENIC_CPP_RESOURCES_H_
+#define LIB_UI_SCENIC_CPP_RESOURCES_H_
+
+#include "lib/ui/scenic/cpp/session.h"
+
+#include <zircon/assert.h>
+
+namespace scenic {
+
+// Represents a resource in a session with a dynamically allocated id.
+// The resource is released from the session when this object is destroyed
+// but it may still be in use within the session if other resources reference
+// it.
+// This type cannot be instantiated, please see subclasses.
+class Resource {
+ public:
+ // Gets the session which owns this resource.
+ Session* session() const {
+ ZX_DEBUG_ASSERT(session_);
+ return session_;
+ }
+
+ // Gets the resource's id.
+ uint32_t id() const { return id_; }
+
+ // Exports the resource and associates it with |export_token|.
+ void Export(zx::eventpair export_token);
+
+ // Exports the resource and returns an import token in |out_import_token|
+ // which allows it to be imported into other sessions.
+ void ExportAsRequest(zx::eventpair* out_import_token);
+
+ // Sets which events a resource should deliver to the session listener.
+ void SetEventMask(uint32_t event_mask);
+
+ // Sets a label to help developers identify the purpose of the resource
+ // when using diagnostic tools.
+ void SetLabel(const std::string& label);
+
+ protected:
+ explicit Resource(Session* session);
+ Resource(Resource&& moved);
+
+ Resource(const Resource&) = delete;
+ Resource& operator=(const Resource&) = delete;
+
+ virtual ~Resource();
+
+ private:
+ Session* const session_;
+ uint32_t const id_;
+};
+
+// Represents a memory resource in a session.
+// TODO(MZ-268): Make this class final, and add public move constructor.
+class Memory : public Resource {
+ public:
+ Memory(Session* session, zx::vmo vmo, uint64_t allocation_size,
+ fuchsia::images::MemoryType memory_type);
+ ~Memory();
+
+ // Gets the underlying VMO's memory type, indicating whether it represents
+ // host or GPU memory.
+ fuchsia::images::MemoryType memory_type() const { return memory_type_; }
+
+ protected:
+ Memory(Memory&& moved);
+
+ private:
+ fuchsia::images::MemoryType const memory_type_;
+};
+
+// Represents an abstract shape resource in a session.
+// This type cannot be instantiated, please see subclasses.
+class Shape : public Resource {
+ protected:
+ explicit Shape(Session* session);
+ Shape(Shape&& moved);
+ ~Shape();
+};
+
+// Represents a circle shape resource in a session.
+class Circle final : public Shape {
+ public:
+ Circle(Session* session, float radius);
+ Circle(Circle&& moved);
+ ~Circle();
+};
+
+// Represents a rectangle shape resource in a session.
+class Rectangle final : public Shape {
+ public:
+ Rectangle(Session* session, float width, float height);
+ Rectangle(Rectangle&& moved);
+ ~Rectangle();
+};
+
+// Represents a rounded rectangle shape resource in a session.
+class RoundedRectangle final : public Shape {
+ public:
+ RoundedRectangle(Session* session, float width, float height,
+ float top_left_radius, float top_right_radius,
+ float bottom_right_radius, float bottom_left_radius);
+ RoundedRectangle(RoundedRectangle&& moved);
+ ~RoundedRectangle();
+};
+
+// Represents an image resource in a session.
+// TODO(MZ-268): Make this class final, and add public move constructor.
+class Image : public Resource {
+ public:
+ // Creates an image resource bound to a session.
+ Image(const Memory& memory, off_t memory_offset,
+ fuchsia::images::ImageInfo info);
+ Image(Session* session, uint32_t memory_id, off_t memory_offset,
+ fuchsia::images::ImageInfo info);
+ ~Image();
+
+ // Returns the number of bytes needed to represent an image.
+ static size_t ComputeSize(const fuchsia::images::ImageInfo& image_info);
+
+ // Gets the byte offset of the image within its memory resource.
+ off_t memory_offset() const { return memory_offset_; }
+
+ // Gets information about the image's layout.
+ const fuchsia::images::ImageInfo& info() const { return info_; }
+
+ protected:
+ Image(Image&& moved);
+
+ private:
+ off_t const memory_offset_;
+ fuchsia::images::ImageInfo const info_;
+};
+
+// Represents a buffer that is immutably bound to a range of a memory resource.
+class Buffer final : public Resource {
+ public:
+ Buffer(const Memory& memory, off_t memory_offset, size_t buffer_size);
+ Buffer(Session* session, uint32_t memory_id, off_t memory_offset,
+ size_t buffer_size);
+ Buffer(Buffer&& moved);
+ ~Buffer();
+};
+
+// Represents a mesh resource in a session. Before it can be rendered, it
+// must be bound to index and vertex arrays by calling the BindBuffers() method.
+class Mesh final : public Shape {
+ public:
+ Mesh(Session* session);
+ Mesh(Mesh&& moved);
+ ~Mesh();
+
+ // These arguments are documented in commands.fidl; see
+ // BindMeshBuffersCmd.
+ void BindBuffers(const Buffer& index_buffer,
+ fuchsia::ui::gfx::MeshIndexFormat index_format,
+ uint64_t index_offset, uint32_t index_count,
+ const Buffer& vertex_buffer,
+ fuchsia::ui::gfx::MeshVertexFormat vertex_format,
+ uint64_t vertex_offset, uint32_t vertex_count,
+ const float bounding_box_min[3],
+ const float bounding_box_max[3]);
+};
+
+// Represents a material resource in a session.
+class Material final : public Resource {
+ public:
+ explicit Material(Session* session);
+ Material(Material&& moved);
+ ~Material();
+
+ // Sets the material's texture.
+ void SetTexture(const Image& image) {
+ ZX_DEBUG_ASSERT(session() == image.session());
+ SetTexture(image.id());
+ }
+ void SetTexture(uint32_t image_id);
+
+ // Sets the material's color.
+ void SetColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha);
+};
+
+// Represents an abstract node resource in a session.
+// This type cannot be instantiated, please see subclasses.
+class Node : public Resource {
+ public:
+ // Sets the node's transform properties.
+ void SetTranslation(float tx, float ty, float tz) {
+ SetTranslation((float[3]){tx, ty, tz});
+ }
+ void SetTranslation(const float translation[3]);
+ void SetTranslation(uint32_t variable_id);
+ void SetScale(float sx, float sy, float sz) {
+ SetScale((float[3]){sx, sy, sz});
+ }
+ void SetScale(const float scale[3]);
+ void SetScale(uint32_t variable_id);
+ void SetRotation(float qi, float qj, float qk, float qw) {
+ SetRotation((float[4]){qi, qj, qk, qw});
+ }
+ void SetRotation(const float quaternion[4]);
+ void SetRotation(uint32_t variable_id);
+ void SetAnchor(float ax, float ay, float az) {
+ SetAnchor((float[3]){ax, ay, az});
+ }
+ void SetAnchor(const float anchor[3]);
+ void SetAnchor(uint32_t variable_id);
+
+ void SendSizeChangeHint(float width_change_factor,
+ float height_change_factor);
+
+ // Sets the node's tag value.
+ void SetTag(uint32_t tag_value);
+
+ // Sets the node's hit test behavior.
+ void SetHitTestBehavior(fuchsia::ui::gfx::HitTestBehavior hit_test_behavior);
+
+ // Detaches the node from its parent.
+ void Detach();
+
+ protected:
+ explicit Node(Session* session);
+ Node(Node&& moved);
+ ~Node();
+};
+
+// Represents an shape node resource in a session.
+class ShapeNode final : public Node {
+ public:
+ explicit ShapeNode(Session* session);
+ ShapeNode(ShapeNode&& moved);
+ ~ShapeNode();
+
+ // Sets the shape that the shape node should draw.
+ void SetShape(const Shape& shape) {
+ ZX_DEBUG_ASSERT(session() == shape.session());
+ SetShape(shape.id());
+ }
+ void SetShape(uint32_t shape_id);
+
+ // Sets the material with which to draw the shape.
+ void SetMaterial(const Material& material) {
+ ZX_DEBUG_ASSERT(session() == material.session());
+ SetMaterial(material.id());
+ }
+ void SetMaterial(uint32_t material_id);
+};
+
+// Abstract base class for nodes which can have child nodes.
+// This type cannot be instantiated, please see subclasses.
+class ContainerNode : public Node {
+ public:
+ // Adds a child to the node.
+ void AddChild(const Node& child) {
+ ZX_DEBUG_ASSERT(session() == child.session());
+ AddChild(child.id());
+ }
+ void AddChild(uint32_t child_node_id);
+
+ void AddPart(const Node& part) {
+ ZX_DEBUG_ASSERT(session() == part.session());
+ AddPart(part.id());
+ }
+ void AddPart(uint32_t part_node_id);
+
+ // Detaches all children from the node.
+ void DetachChildren();
+
+ protected:
+ explicit ContainerNode(Session* session);
+ ContainerNode(ContainerNode&& moved);
+ ~ContainerNode();
+};
+
+// Required by EntityNode::Attach().
+class ViewHolder;
+
+// Represents an entity node resource in a session.
+// TODO(MZ-268): Make this class final, and add public move constructor.
+class EntityNode : public ContainerNode {
+ public:
+ explicit EntityNode(Session* session);
+ ~EntityNode();
+
+ void SetClip(uint32_t clip_id, bool clip_to_self);
+
+ void Attach(const ViewHolder& view_holder);
+
+ void Snapshot(fuchsia::ui::gfx::SnapshotCallbackHACKPtr callback);
+};
+
+// Represents an imported node resource in a session.
+// The imported node is initially created in an unbound state and must
+// be bound immediately after creation, prior to use.
+class ImportNode final : public ContainerNode {
+ public:
+ explicit ImportNode(Session* session);
+ ImportNode(ImportNode&& moved);
+ ~ImportNode();
+
+ // Imports the node associated with |import_token|.
+ void Bind(zx::eventpair import_token);
+
+ // Imports the node and returns an export token in |out_export_token|
+ // by which another session can export a node to associate with this import.
+ void BindAsRequest(zx::eventpair* out_export_token);
+
+ // Returns true if the import has been bound.
+ bool is_bound() const { return is_bound_; }
+
+ private:
+ bool is_bound_ = false;
+};
+
+// Represents a proxy for a View which can be added to a scene graph in order
+// to embed the View within it.
+//
+// Each ViewHolder is linked to a paired View via a shared token.
+//
+// Usually the ViewHolder and its associated View exist in separate processes,
+// allowing a distributed scene graph to be constructed.
+class ViewHolder final : public Resource {
+ public:
+ ViewHolder(Session* session, zx::eventpair token,
+ const std::string& debug_name);
+ ~ViewHolder();
+
+ // Set properties of the attached view.
+
+ void SetViewProperties(float min_x, float min_y, float min_z, float max_x,
+ float max_y, float max_z, float in_min_x,
+ float in_min_y, float in_min_z, float in_max_x,
+ float in_max_y, float in_max_z) {
+ SetViewProperties((float[3]){min_x, min_y, min_z},
+ (float[3]){max_x, max_y, max_z},
+ (float[3]){in_min_x, in_min_y, in_min_z},
+ (float[3]){in_max_x, in_max_y, in_max_z});
+ }
+ void SetViewProperties(const float bounding_box_min[3],
+ const float bounding_box_max[3],
+ const float inset_from_min[3],
+ const float inset_from_max[3]);
+ void SetViewProperties(const fuchsia::ui::gfx::ViewProperties& props);
+};
+
+// Represents a transform space which serves as a container for Nodes. The
+// Nodes will have the Views' coordinate transform applied to their own, in
+// addition to being clipped to the Views' bounding box.
+class View final : public Resource {
+ public:
+ View(Session* session, zx::eventpair token, const std::string& debug_name);
+ ~View();
+
+ void AddChild(const Node& child) const;
+ void DetachChild(const Node& child) const;
+};
+
+// Creates a node that clips the contents of its hierarchy to the specified clip
+// shape.
+class ClipNode final : public ContainerNode {
+ public:
+ explicit ClipNode(Session* session);
+ ClipNode(ClipNode&& moved);
+ ~ClipNode();
+};
+
+// Creates a node that renders its hierarchy with the specified opacity.
+class OpacityNode final : public ContainerNode {
+ public:
+ explicit OpacityNode(Session* session);
+ OpacityNode(OpacityNode&& moved);
+ ~OpacityNode();
+
+ // The opacity with which to render the contents of the hierarchy rooted at
+ // this node. The opacity values are clamped 0.0 to 1.0.
+ void SetOpacity(float opacity);
+};
+
+// A value that can be used in place of a constant value.
+class Variable final : public Resource {
+ public:
+ explicit Variable(Session* session, fuchsia::ui::gfx::Value initial_value);
+ Variable(Variable&& moved);
+ ~Variable();
+};
+
+// Represents an abstract light resource in a session.
+// This type cannot be instantiated, please see subclasses.
+class Light : public Resource {
+ public:
+ // Sets the light's color.
+ void SetColor(float red, float green, float blue) {
+ SetColor((float[3]){red, green, blue});
+ }
+ void SetColor(const float rgb[3]);
+ void SetColor(uint32_t variable_id);
+
+ // Detach light from the scene it is attached to, if any.
+ void Detach();
+
+ protected:
+ explicit Light(Session* session);
+ Light(Light&& moved);
+ ~Light();
+};
+
+// Represents a directional light resource in a session.
+class AmbientLight final : public Light {
+ public:
+ explicit AmbientLight(Session* session);
+ AmbientLight(AmbientLight&& moved);
+ ~AmbientLight();
+};
+
+// Represents a directional light resource in a session.
+class DirectionalLight final : public Light {
+ public:
+ explicit DirectionalLight(Session* session);
+ DirectionalLight(DirectionalLight&& moved);
+ ~DirectionalLight();
+
+ // Sets the light's direction.
+ void SetDirection(float dx, float dy, float dz) {
+ SetDirection((float[3]){dx, dy, dz});
+ }
+ void SetDirection(const float direction[3]);
+ void SetDirection(uint32_t variable_id);
+};
+
+// Represents a scene resource in a session.
+class Scene final : public ContainerNode {
+ public:
+ explicit Scene(Session* session);
+ Scene(Scene&& moved);
+ ~Scene();
+
+ void AddLight(const Light& light) {
+ ZX_DEBUG_ASSERT(session() == light.session());
+ AddLight(light.id());
+ }
+ void AddLight(uint32_t light_id);
+ void DetachLights();
+
+ private:
+ void Detach() = delete;
+};
+
+class CameraBase : public Resource {
+ public:
+ CameraBase(Session* session) : Resource(session) {}
+ CameraBase(CameraBase&& moved) : Resource(std::move(moved)) {}
+ ~CameraBase() {}
+ // Sets the camera's view parameters.
+ void SetTransform(const float eye_position[3], const float eye_look_at[3],
+ const float eye_up[3]);
+ // Sets the camera pose buffer
+ void SetPoseBuffer(const Buffer& buffer, uint32_t num_entries,
+ int64_t base_time, uint64_t time_interval);
+};
+
+// Represents a camera resource in a session.
+class Camera : public CameraBase {
+ public:
+ explicit Camera(const Scene& scene);
+ Camera(Session* session, uint32_t scene_id);
+ Camera(Camera&& moved);
+ ~Camera();
+
+ // Sets the camera's projection parameters.
+ void SetProjection(const float fovy);
+};
+
+// Represents a StereoCamera resource in a session.
+class StereoCamera final : public CameraBase {
+ public:
+ explicit StereoCamera(const Scene& scene);
+ StereoCamera(Session* session, uint32_t scene_id);
+ StereoCamera(StereoCamera&& moved);
+ ~StereoCamera();
+
+ // Sets the camera's projection parameters.
+ void SetStereoProjection(const float left_projection[16],
+ const float right_projection[16]);
+};
+
+// Represents a renderer resource in a session.
+class Renderer final : public Resource {
+ public:
+ explicit Renderer(Session* session);
+ Renderer(Renderer&& moved);
+ ~Renderer();
+
+ // Sets the camera whose view will be rendered.
+ void SetCamera(const Camera& camera) {
+ ZX_DEBUG_ASSERT(session() == camera.session());
+ SetCamera(camera.id());
+ }
+ void SetCamera(uint32_t camera_id);
+
+ void SetParam(fuchsia::ui::gfx::RendererParam param);
+
+ // Convenient wrapper for SetParam().
+ void SetShadowTechnique(fuchsia::ui::gfx::ShadowTechnique technique);
+
+ // Set whether clipping is disabled for this renderer.
+ // NOTE: disabling clipping only has a visual effect; hit-testing is not
+ // affected.
+ void SetDisableClipping(bool disable_clipping);
+};
+
+// Represents a layer resource in a session.
+class Layer final : public Resource {
+ public:
+ explicit Layer(Session* session);
+ Layer(Layer&& moved);
+ ~Layer();
+
+ // Sets the layer's XY translation and Z-order.
+ void SetTranslation(float tx, float ty, float tz) {
+ SetTranslation((float[3]){tx, ty, tz});
+ }
+ void SetTranslation(const float translation[3]);
+
+ void SetSize(float width, float height) {
+ SetSize((float[2]){width, height});
+ }
+ void SetSize(const float size[2]);
+
+ void SetRenderer(const Renderer& renderer) {
+ ZX_DEBUG_ASSERT(session() == renderer.session());
+ SetRenderer(renderer.id());
+ }
+ void SetRenderer(uint32_t renderer_id);
+};
+
+// Represents a layer-stack resource in a session.
+class LayerStack final : public Resource {
+ public:
+ explicit LayerStack(Session* session);
+ LayerStack(LayerStack&& moved);
+ ~LayerStack();
+
+ void AddLayer(const Layer& layer) {
+ ZX_DEBUG_ASSERT(session() == layer.session());
+ AddLayer(layer.id());
+ }
+ void AddLayer(uint32_t layer_id);
+ void RemoveLayer(const Layer& layer) {
+ ZX_DEBUG_ASSERT(session() == layer.session());
+ RemoveLayer(layer.id());
+ }
+ void RemoveLayer(uint32_t layer_id);
+ void RemoveAllLayers();
+};
+
+// Represents a display-compositor resource in a session.
+class DisplayCompositor final : public Resource {
+ public:
+ explicit DisplayCompositor(Session* session);
+ DisplayCompositor(DisplayCompositor&& moved);
+ ~DisplayCompositor();
+
+ // Sets the layer-stack that is to be composited.
+ void SetLayerStack(const LayerStack& layer_stack) {
+ ZX_DEBUG_ASSERT(session() == layer_stack.session());
+ SetLayerStack(layer_stack.id());
+ }
+ void SetLayerStack(uint32_t layer_stack_id);
+};
+
+// Represents a display-less compositor resource in a session.
+class Compositor final : public Resource {
+ public:
+ explicit Compositor(Session* session);
+ Compositor(Compositor&& moved);
+ ~Compositor();
+
+ // Sets the layer-stack that is to be composited.
+ void SetLayerStack(const LayerStack& layer_stack) {
+ ZX_DEBUG_ASSERT(session() == layer_stack.session());
+ SetLayerStack(layer_stack.id());
+ }
+ void SetLayerStack(uint32_t layer_stack_id);
+};
+
+} // namespace scenic
+
+#endif // LIB_UI_SCENIC_CPP_RESOURCES_H_
diff --git a/pkg/scenic_cpp/include/lib/ui/scenic/cpp/session.h b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/session.h
new file mode 100644
index 0000000..3455d15
--- /dev/null
+++ b/pkg/scenic_cpp/include/lib/ui/scenic/cpp/session.h
@@ -0,0 +1,148 @@
+// Copyright 2017 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.
+
+#ifndef LIB_UI_SCENIC_CPP_SESSION_H_
+#define LIB_UI_SCENIC_CPP_SESSION_H_
+
+#include <fuchsia/images/cpp/fidl.h>
+#include <fuchsia/ui/gfx/cpp/fidl.h>
+#include <fuchsia/ui/input/cpp/fidl.h>
+#include <fuchsia/ui/scenic/cpp/fidl.h>
+#include <lib/fit/function.h>
+#include <lib/zx/event.h>
+
+#include <utility>
+
+#include "lib/fidl/cpp/binding.h"
+
+namespace scenic {
+
+// Connect to Scenic and establish a new Session, as well as an InterfaceRequest
+// for a SessionListener that can be hooked up as desired.
+using SessionPtrAndListenerRequest =
+ std::pair<fuchsia::ui::scenic::SessionPtr,
+ fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>>;
+SessionPtrAndListenerRequest CreateScenicSessionPtrAndListenerRequest(
+ fuchsia::ui::scenic::Scenic* scenic);
+
+// Wraps a Scenic session.
+// Maintains a queue of pending operations and assists with allocation of
+// resource ids.
+class Session : private fuchsia::ui::scenic::SessionListener {
+ public:
+ // Provides timing information about a presentation request which has
+ // been applied by the scene manager.
+ using PresentCallback =
+ fit::function<void(fuchsia::images::PresentationInfo info)>;
+
+ // Provide information about hits.
+ using HitTestCallback =
+ fit::function<void(fidl::VectorPtr<fuchsia::ui::gfx::Hit> hits)>;
+
+ // Called when session events are received.
+ using EventHandler =
+ fit::function<void(fidl::VectorPtr<fuchsia::ui::scenic::Event>)>;
+
+ // Wraps the provided session and session listener.
+ // The listener is optional.
+ explicit Session(fuchsia::ui::scenic::SessionPtr session,
+ fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
+ session_listener = nullptr);
+
+ // Creates a new session using the provided Scenic and binds the listener to
+ // this object. The Scenic itself is not retained after construction.
+ explicit Session(fuchsia::ui::scenic::Scenic* scenic);
+
+ explicit Session(SessionPtrAndListenerRequest session_and_listener);
+
+ Session(const Session&) = delete;
+ Session& operator=(const Session&) = delete;
+
+ // Destroys the session.
+ // All resources must be released prior to destruction.
+ ~Session();
+
+ void set_error_handler(fit::function<void(zx_status_t)> closure) {
+ session_.set_error_handler(std::move(closure));
+ }
+
+ // Sets a callback which is invoked when events are received.
+ void set_event_handler(EventHandler event_handler) {
+ event_handler_ = std::move(event_handler);
+ }
+
+ // Gets a pointer to the underlying session interface.
+ fuchsia::ui::scenic::Session* session() { return session_.get(); }
+
+ // Allocates a new unique resource id.
+ uint32_t AllocResourceId();
+
+ // Enqueues an operation to release a resource.
+ void ReleaseResource(uint32_t resource_id);
+
+ // Enqueues an operation.
+ // The session will queue operations locally to batch submission of operations
+ // until |Flush()| or |Present()| is called.
+ void Enqueue(fuchsia::ui::scenic::Command command);
+ void Enqueue(fuchsia::ui::gfx::Command command);
+ void Enqueue(fuchsia::ui::input::Command command);
+
+ // Registers an acquire fence to be submitted during the subsequent call to
+ // |Present()|.
+ void EnqueueAcquireFence(zx::event fence);
+
+ // Registers a release fence to be submitted during the subsequent call to
+ // |Present()|.
+ void EnqueueReleaseFence(zx::event fence);
+
+ // Flushes queued operations to the session.
+ void Flush();
+
+ // Presents all previously enqueued operations.
+ // Implicitly flushes all queued operations to the session.
+ // Invokes the callback when the scene manager applies the presentation.
+ void Present(uint64_t presentation_time, PresentCallback callback);
+
+ // Performs a hit test along the specified ray.
+ void HitTest(uint32_t node_id, const float ray_origin[3],
+ const float ray_direction[3], HitTestCallback callback);
+
+ // Performs a hit test along the specified ray into the engine's first
+ // compositor.
+ void HitTestDeviceRay(
+ const float ray_origin[3], const float ray_direction[3],
+ fuchsia::ui::scenic::Session::HitTestDeviceRayCallback callback);
+
+ // Unbinds the internal SessionPtr; this allows moving this across threads.
+ void Unbind();
+
+ // Rebinds the Session interface internally; this must be called after a call
+ // to Unbind().
+ void Rebind();
+
+ void SetDebugName(const std::string& debug_name);
+
+ private:
+ // |fuchsia::ui::scenic::SessionListener|
+ void OnScenicError(fidl::StringPtr error) override;
+ void OnScenicEvent(
+ fidl::VectorPtr<fuchsia::ui::scenic::Event> events) override;
+
+ fuchsia::ui::scenic::SessionPtr session_;
+ // |session_handle_| is stored only when |session_| is unbound/invalid.
+ fidl::InterfaceHandle<fuchsia::ui::scenic::Session> session_handle_;
+ uint32_t next_resource_id_ = 1u;
+ uint32_t resource_count_ = 0u;
+
+ fidl::VectorPtr<fuchsia::ui::scenic::Command> commands_;
+ fidl::VectorPtr<zx::event> acquire_fences_;
+ fidl::VectorPtr<zx::event> release_fences_;
+
+ EventHandler event_handler_;
+ fidl::Binding<fuchsia::ui::scenic::SessionListener> session_listener_binding_;
+};
+
+} // namespace scenic
+
+#endif // LIB_UI_SCENIC_CPP_SESSION_H_
diff --git a/pkg/scenic_cpp/meta.json b/pkg/scenic_cpp/meta.json
new file mode 100644
index 0000000..3c966c8
--- /dev/null
+++ b/pkg/scenic_cpp/meta.json
@@ -0,0 +1,31 @@
+{
+ "deps": [
+ "fidl_cpp",
+ "images_cpp",
+ "fit",
+ "zx"
+ ],
+ "fidl_deps": [
+ "fuchsia.ui.gfx",
+ "fuchsia.ui.scenic"
+ ],
+ "headers": [
+ "pkg/scenic_cpp/include/lib/ui/scenic/cpp/commands.h",
+ "pkg/scenic_cpp/include/lib/ui/scenic/cpp/host_image_cycler.h",
+ "pkg/scenic_cpp/include/lib/ui/scenic/cpp/host_memory.h",
+ "pkg/scenic_cpp/include/lib/ui/scenic/cpp/id.h",
+ "pkg/scenic_cpp/include/lib/ui/scenic/cpp/resources.h",
+ "pkg/scenic_cpp/include/lib/ui/scenic/cpp/session.h"
+ ],
+ "include_dir": "pkg/scenic_cpp/include",
+ "name": "scenic_cpp",
+ "root": "pkg/scenic_cpp",
+ "sources": [
+ "pkg/scenic_cpp/commands.cc",
+ "pkg/scenic_cpp/host_image_cycler.cc",
+ "pkg/scenic_cpp/host_memory.cc",
+ "pkg/scenic_cpp/resources.cc",
+ "pkg/scenic_cpp/session.cc"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/scenic_cpp/resources.cc b/pkg/scenic_cpp/resources.cc
new file mode 100644
index 0000000..58ab0d5
--- /dev/null
+++ b/pkg/scenic_cpp/resources.cc
@@ -0,0 +1,570 @@
+// Copyright 2017 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/ui/scenic/cpp/resources.h"
+
+#include <algorithm>
+
+#include "lib/images/cpp/images.h"
+#include "lib/ui/scenic/cpp/commands.h"
+
+namespace scenic {
+namespace {
+
+template <class T>
+constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
+ return (v < lo) ? lo : (hi < v) ? hi : v;
+}
+
+} // namespace
+
+Resource::Resource(Session* session)
+ : session_(session), id_(session->AllocResourceId()) {}
+
+Resource::Resource(Resource&& moved)
+ : session_(moved.session_), id_(moved.id_) {
+ auto& moved_session = *const_cast<Session**>(&moved.session_);
+ auto& moved_id = *const_cast<uint32_t*>(&moved.id_);
+ moved_session = nullptr;
+ moved_id = 0;
+}
+
+Resource::~Resource() {
+ // If this resource was moved, it is not responsible for releasing the ID.
+ if (session_)
+ session_->ReleaseResource(id_);
+}
+
+void Resource::Export(zx::eventpair export_token) {
+ session_->Enqueue(NewExportResourceCmd(id(), std::move(export_token)));
+}
+
+void Resource::ExportAsRequest(zx::eventpair* out_import_token) {
+ session_->Enqueue(NewExportResourceCmdAsRequest(id(), out_import_token));
+}
+
+void Resource::SetEventMask(uint32_t event_mask) {
+ session_->Enqueue(NewSetEventMaskCmd(id(), event_mask));
+}
+
+void Resource::SetLabel(const std::string& label) {
+ session_->Enqueue(NewSetLabelCmd(id(), label));
+}
+
+Shape::Shape(Session* session) : Resource(session) {}
+
+Shape::Shape(Shape&& moved) : Resource(std::move(moved)) {}
+
+Shape::~Shape() = default;
+
+Circle::Circle(Session* session, float radius) : Shape(session) {
+ session->Enqueue(NewCreateCircleCmd(id(), radius));
+}
+
+Circle::Circle(Circle&& moved) : Shape(std::move(moved)) {}
+
+Circle::~Circle() = default;
+
+Rectangle::Rectangle(Session* session, float width, float height)
+ : Shape(session) {
+ session->Enqueue(NewCreateRectangleCmd(id(), width, height));
+}
+
+Rectangle::Rectangle(Rectangle&& moved) : Shape(std::move(moved)) {}
+
+Rectangle::~Rectangle() = default;
+
+RoundedRectangle::RoundedRectangle(Session* session, float width, float height,
+ float top_left_radius,
+ float top_right_radius,
+ float bottom_right_radius,
+ float bottom_left_radius)
+ : Shape(session) {
+ session->Enqueue(NewCreateRoundedRectangleCmd(
+ id(), width, height, top_left_radius, top_right_radius,
+ bottom_right_radius, bottom_left_radius));
+}
+
+RoundedRectangle::RoundedRectangle(RoundedRectangle&& moved)
+ : Shape(std::move(moved)) {}
+
+RoundedRectangle::~RoundedRectangle() = default;
+
+Image::Image(const Memory& memory, off_t memory_offset,
+ fuchsia::images::ImageInfo info)
+ : Image(memory.session(), memory.id(), memory_offset, std::move(info)) {}
+
+Image::Image(Session* session, uint32_t memory_id, off_t memory_offset,
+ fuchsia::images::ImageInfo info)
+ : Resource(session), memory_offset_(memory_offset), info_(info) {
+ session->Enqueue(
+ NewCreateImageCmd(id(), memory_id, memory_offset_, std::move(info)));
+}
+
+Image::Image(Image&& moved)
+ : Resource(std::move(moved)),
+ memory_offset_(moved.memory_offset_),
+ info_(moved.info_) {}
+
+Image::~Image() = default;
+
+size_t Image::ComputeSize(const fuchsia::images::ImageInfo& image_info) {
+ return images::ImageSize(image_info);
+}
+
+Buffer::Buffer(const Memory& memory, off_t memory_offset, size_t num_bytes)
+ : Buffer(memory.session(), memory.id(), memory_offset, num_bytes) {}
+
+Buffer::Buffer(Session* session, uint32_t memory_id, off_t memory_offset,
+ size_t num_bytes)
+ : Resource(session) {
+ session->Enqueue(
+ NewCreateBufferCmd(id(), memory_id, memory_offset, num_bytes));
+}
+
+Buffer::Buffer(Buffer&& moved) : Resource(std::move(moved)) {}
+
+Buffer::~Buffer() = default;
+
+Memory::Memory(Session* session, zx::vmo vmo, uint64_t allocation_size,
+ fuchsia::images::MemoryType memory_type)
+ : Resource(session), memory_type_(memory_type) {
+ session->Enqueue(
+ NewCreateMemoryCmd(id(), std::move(vmo), allocation_size, memory_type));
+}
+
+Memory::Memory(Memory&& moved)
+ : Resource(std::move(moved)), memory_type_(moved.memory_type_) {}
+
+Memory::~Memory() = default;
+
+Mesh::Mesh(Session* session) : Shape(session) {
+ session->Enqueue(NewCreateMeshCmd(id()));
+}
+
+Mesh::Mesh(Mesh&& moved) : Shape(std::move(moved)) {}
+
+Mesh::~Mesh() = default;
+
+void Mesh::BindBuffers(const Buffer& index_buffer,
+ fuchsia::ui::gfx::MeshIndexFormat index_format,
+ uint64_t index_offset, uint32_t index_count,
+ const Buffer& vertex_buffer,
+ fuchsia::ui::gfx::MeshVertexFormat vertex_format,
+ uint64_t vertex_offset, uint32_t vertex_count,
+ const float bounding_box_min[3],
+ const float bounding_box_max[3]) {
+ ZX_DEBUG_ASSERT(session() == index_buffer.session() &&
+ session() == vertex_buffer.session());
+ session()->Enqueue(NewBindMeshBuffersCmd(
+ id(), index_buffer.id(), index_format, index_offset, index_count,
+ vertex_buffer.id(), std::move(vertex_format), vertex_offset, vertex_count,
+ bounding_box_min, bounding_box_max));
+}
+
+Material::Material(Session* session) : Resource(session) {
+ session->Enqueue(NewCreateMaterialCmd(id()));
+}
+
+Material::Material(Material&& moved) : Resource(std::move(moved)) {}
+
+Material::~Material() = default;
+
+void Material::SetTexture(uint32_t image_id) {
+ session()->Enqueue(NewSetTextureCmd(id(), image_id));
+}
+
+void Material::SetColor(uint8_t red, uint8_t green, uint8_t blue,
+ uint8_t alpha) {
+ session()->Enqueue(NewSetColorCmd(id(), red, green, blue, alpha));
+}
+
+Node::Node(Session* session) : Resource(session) {}
+
+Node::Node(Node&& moved) : Resource(std::move(moved)) {}
+
+Node::~Node() = default;
+
+void Node::SetTranslation(const float translation[3]) {
+ session()->Enqueue(NewSetTranslationCmd(id(), translation));
+}
+
+void Node::SetTranslation(uint32_t variable_id) {
+ session()->Enqueue(NewSetTranslationCmd(id(), variable_id));
+}
+
+void Node::SetScale(const float scale[3]) {
+ session()->Enqueue(NewSetScaleCmd(id(), scale));
+}
+
+void Node::SetScale(uint32_t variable_id) {
+ session()->Enqueue(NewSetScaleCmd(id(), variable_id));
+}
+
+void Node::SetRotation(const float quaternion[4]) {
+ session()->Enqueue(NewSetRotationCmd(id(), quaternion));
+}
+
+void Node::SetRotation(uint32_t variable_id) {
+ session()->Enqueue(NewSetRotationCmd(id(), variable_id));
+}
+
+void Node::SetAnchor(const float anchor[3]) {
+ session()->Enqueue(NewSetAnchorCmd(id(), anchor));
+}
+
+void Node::SetAnchor(uint32_t variable_id) {
+ session()->Enqueue(NewSetAnchorCmd(id(), variable_id));
+}
+
+void Node::SendSizeChangeHint(float width_change_factor,
+ float height_change_factor) {
+ session()->Enqueue(NewSendSizeChangeHintCmdHACK(id(), width_change_factor,
+ height_change_factor));
+}
+
+void Node::SetTag(uint32_t tag_value) {
+ session()->Enqueue(NewSetTagCmd(id(), tag_value));
+}
+
+void Node::SetHitTestBehavior(
+ fuchsia::ui::gfx::HitTestBehavior hit_test_behavior) {
+ session()->Enqueue(NewSetHitTestBehaviorCmd(id(), hit_test_behavior));
+}
+
+void Node::Detach() { session()->Enqueue(NewDetachCmd(id())); }
+
+ShapeNode::ShapeNode(Session* session) : Node(session) {
+ session->Enqueue(NewCreateShapeNodeCmd(id()));
+}
+
+ShapeNode::ShapeNode(ShapeNode&& moved) : Node(std::move(moved)) {}
+
+ShapeNode::~ShapeNode() = default;
+
+void ShapeNode::SetShape(uint32_t shape_id) {
+ session()->Enqueue(NewSetShapeCmd(id(), shape_id));
+}
+
+void ShapeNode::SetMaterial(uint32_t material_id) {
+ session()->Enqueue(NewSetMaterialCmd(id(), material_id));
+}
+
+ContainerNode::ContainerNode(Session* session) : Node(session) {}
+
+ContainerNode::ContainerNode(ContainerNode&& moved) : Node(std::move(moved)) {}
+
+ContainerNode::~ContainerNode() = default;
+
+void ContainerNode::AddChild(uint32_t child_node_id) {
+ session()->Enqueue(NewAddChildCmd(id(), child_node_id));
+}
+
+void ContainerNode::AddPart(uint32_t part_node_id) {
+ session()->Enqueue(NewAddPartCmd(id(), part_node_id));
+}
+
+void ContainerNode::DetachChildren() {
+ session()->Enqueue(NewDetachChildrenCmd(id()));
+}
+
+EntityNode::EntityNode(Session* session) : ContainerNode(session) {
+ session->Enqueue(NewCreateEntityNodeCmd(id()));
+}
+
+void EntityNode::Attach(const ViewHolder& view_holder) {
+ session()->Enqueue(NewAddChildCmd(id(), view_holder.id()));
+}
+
+void EntityNode::Snapshot(fuchsia::ui::gfx::SnapshotCallbackHACKPtr callback) {
+ session()->Enqueue(NewTakeSnapshotCmdHACK(id(), std::move(callback)));
+}
+
+EntityNode::~EntityNode() = default;
+
+void EntityNode::SetClip(uint32_t clip_id, bool clip_to_self) {
+ session()->Enqueue(NewSetClipCmd(id(), clip_id, clip_to_self));
+}
+
+ImportNode::ImportNode(Session* session) : ContainerNode(session) {}
+
+ImportNode::ImportNode(ImportNode&& moved) : ContainerNode(std::move(moved)) {}
+
+ImportNode::~ImportNode() {
+ ZX_DEBUG_ASSERT_MSG(is_bound_, "Import was never bound.");
+}
+
+void ImportNode::Bind(zx::eventpair import_token) {
+ ZX_DEBUG_ASSERT(!is_bound_);
+ session()->Enqueue(NewImportResourceCmd(
+ id(), fuchsia::ui::gfx::ImportSpec::NODE, std::move(import_token)));
+ is_bound_ = true;
+}
+
+void ImportNode::BindAsRequest(zx::eventpair* out_export_token) {
+ ZX_DEBUG_ASSERT(!is_bound_);
+ session()->Enqueue(NewImportResourceCmdAsRequest(
+ id(), fuchsia::ui::gfx::ImportSpec::NODE, out_export_token));
+ is_bound_ = true;
+}
+
+ViewHolder::ViewHolder(Session* session, zx::eventpair token,
+ const std::string& debug_name)
+ : Resource(session) {
+ session->Enqueue(NewCreateViewHolderCmd(id(), std::move(token), debug_name));
+}
+
+ViewHolder::~ViewHolder() = default;
+
+void ViewHolder::SetViewProperties(const float bounding_box_min[3],
+ const float bounding_box_max[3],
+ const float inset_from_min[3],
+ const float inset_from_max[3]) {
+ session()->Enqueue(NewSetViewPropertiesCmd(id(), bounding_box_min,
+ bounding_box_max, inset_from_min,
+ inset_from_max));
+}
+
+void ViewHolder::SetViewProperties(
+ const fuchsia::ui::gfx::ViewProperties& props) {
+ session()->Enqueue(NewSetViewPropertiesCmd(id(), props));
+}
+
+View::View(Session* session, zx::eventpair token, const std::string& debug_name)
+ : Resource(session) {
+ session->Enqueue(NewCreateViewCmd(id(), std::move(token), debug_name));
+}
+
+View::~View() = default;
+
+void View::AddChild(const Node& child) const {
+ ZX_DEBUG_ASSERT(session() == child.session());
+ session()->Enqueue(NewAddChildCmd(id(), child.id()));
+}
+
+void View::DetachChild(const Node& child) const {
+ ZX_DEBUG_ASSERT(session() == child.session());
+ session()->Enqueue(NewDetachCmd(child.id()));
+}
+
+ClipNode::ClipNode(Session* session) : ContainerNode(session) {
+ session->Enqueue(NewCreateClipNodeCmd(id()));
+}
+
+ClipNode::ClipNode(ClipNode&& moved) : ContainerNode(std::move(moved)) {}
+
+ClipNode::~ClipNode() = default;
+
+OpacityNode::OpacityNode(Session* session) : ContainerNode(session) {
+ session->Enqueue(NewCreateOpacityNodeCmd(id()));
+}
+
+OpacityNode::OpacityNode(OpacityNode&& moved)
+ : ContainerNode(std::move(moved)) {}
+
+OpacityNode::~OpacityNode() = default;
+
+void OpacityNode::SetOpacity(float opacity) {
+ opacity = clamp(opacity, 0.f, 1.f);
+ session()->Enqueue(NewSetOpacityCmd(id(), opacity));
+}
+
+Variable::Variable(Session* session, fuchsia::ui::gfx::Value initial_value)
+ : Resource(session) {
+ session->Enqueue(NewCreateVariableCmd(id(), std::move(initial_value)));
+}
+
+Variable::Variable(Variable&& moved) : Resource(std::move(moved)) {}
+
+Variable::~Variable() = default;
+
+Scene::Scene(Session* session) : ContainerNode(session) {
+ session->Enqueue(NewCreateSceneCmd(id()));
+}
+
+Scene::Scene(Scene&& moved) : ContainerNode(std::move(moved)) {}
+
+Scene::~Scene() = default;
+
+void Scene::AddLight(uint32_t light_id) {
+ session()->Enqueue(NewAddLightCmd(id(), light_id));
+}
+
+void Scene::DetachLights() { session()->Enqueue(NewDetachLightsCmd(id())); }
+
+void CameraBase::SetTransform(const float eye_position[3],
+ const float eye_look_at[3],
+ const float eye_up[3]) {
+ session()->Enqueue(
+ NewSetCameraTransformCmd(id(), eye_position, eye_look_at, eye_up));
+}
+
+void CameraBase::SetPoseBuffer(const Buffer& buffer, uint32_t num_entries,
+ int64_t base_time, uint64_t time_interval) {
+ session()->Enqueue(NewSetCameraPoseBufferCmd(id(), buffer.id(), num_entries,
+ base_time, time_interval));
+}
+
+Camera::Camera(const Scene& scene) : Camera(scene.session(), scene.id()) {}
+
+Camera::Camera(Session* session, uint32_t scene_id) : CameraBase(session) {
+ session->Enqueue(NewCreateCameraCmd(id(), scene_id));
+}
+
+Camera::Camera(Camera&& moved) : CameraBase(std::move(moved)) {}
+
+Camera::~Camera() = default;
+
+void Camera::SetProjection(const float fovy) {
+ session()->Enqueue(NewSetCameraProjectionCmd(id(), fovy));
+}
+
+StereoCamera::StereoCamera(const Scene& scene)
+ : StereoCamera(scene.session(), scene.id()) {}
+
+StereoCamera::StereoCamera(Session* session, uint32_t scene_id)
+ : CameraBase(session) {
+ session->Enqueue(NewCreateStereoCameraCmd(id(), scene_id));
+}
+
+StereoCamera::StereoCamera(StereoCamera&& moved)
+ : CameraBase(std::move(moved)) {}
+
+StereoCamera::~StereoCamera() = default;
+
+void StereoCamera::SetStereoProjection(const float left_projection[16],
+ const float right_projection[16]) {
+ session()->Enqueue(
+ NewSetStereoCameraProjectionCmd(id(), left_projection, right_projection));
+}
+
+Renderer::Renderer(Session* session) : Resource(session) {
+ session->Enqueue(NewCreateRendererCmd(id()));
+}
+
+Renderer::Renderer(Renderer&& moved) : Resource(std::move(moved)) {}
+
+Renderer::~Renderer() = default;
+
+void Renderer::SetCamera(uint32_t camera_id) {
+ session()->Enqueue(NewSetCameraCmd(id(), camera_id));
+}
+
+void Renderer::SetParam(fuchsia::ui::gfx::RendererParam param) {
+ session()->Enqueue(NewSetRendererParamCmd(id(), std::move(param)));
+}
+
+void Renderer::SetShadowTechnique(fuchsia::ui::gfx::ShadowTechnique technique) {
+ auto param = fuchsia::ui::gfx::RendererParam();
+ param.set_shadow_technique(technique);
+ SetParam(std::move(param));
+}
+
+void Renderer::SetDisableClipping(bool disable_clipping) {
+ session()->Enqueue(NewSetDisableClippingCmd(id(), disable_clipping));
+}
+
+Layer::Layer(Session* session) : Resource(session) {
+ session->Enqueue(NewCreateLayerCmd(id()));
+}
+
+Layer::Layer(Layer&& moved) : Resource(std::move(moved)) {}
+
+Layer::~Layer() = default;
+
+void Layer::SetRenderer(uint32_t renderer_id) {
+ session()->Enqueue(NewSetRendererCmd(id(), renderer_id));
+}
+
+void Layer::SetSize(const float size[2]) {
+ session()->Enqueue(NewSetSizeCmd(id(), size));
+}
+
+LayerStack::LayerStack(Session* session) : Resource(session) {
+ session->Enqueue(NewCreateLayerStackCmd(id()));
+}
+
+LayerStack::LayerStack(LayerStack&& moved) : Resource(std::move(moved)) {}
+
+LayerStack::~LayerStack() = default;
+
+void LayerStack::AddLayer(uint32_t layer_id) {
+ session()->Enqueue(NewAddLayerCmd(id(), layer_id));
+}
+
+void LayerStack::RemoveLayer(uint32_t layer_id) {
+ session()->Enqueue(NewRemoveLayerCmd(id(), layer_id));
+}
+
+void LayerStack::RemoveAllLayers() {
+ session()->Enqueue(NewRemoveAllLayersCmd(id()));
+}
+
+DisplayCompositor::DisplayCompositor(Session* session) : Resource(session) {
+ session->Enqueue(NewCreateDisplayCompositorCmd(id()));
+}
+
+DisplayCompositor::DisplayCompositor(DisplayCompositor&& moved)
+ : Resource(std::move(moved)) {}
+
+DisplayCompositor::~DisplayCompositor() = default;
+
+void DisplayCompositor::SetLayerStack(uint32_t layer_stack_id) {
+ session()->Enqueue(NewSetLayerStackCmd(id(), layer_stack_id));
+}
+
+Compositor::Compositor(Session* session) : Resource(session) {
+ session->Enqueue(NewCreateCompositorCmd(id()));
+}
+
+Compositor::Compositor(Compositor&& moved) : Resource(std::move(moved)) {}
+
+Compositor::~Compositor() = default;
+
+void Compositor::SetLayerStack(uint32_t layer_stack_id) {
+ session()->Enqueue(NewSetLayerStackCmd(id(), layer_stack_id));
+}
+
+Light::Light(Session* session) : Resource(session) {}
+
+Light::Light(Light&& moved) : Resource(std::move(moved)) {}
+
+Light::~Light() = default;
+
+void Light::SetColor(const float rgb[3]) {
+ session()->Enqueue(NewSetLightColorCmd(id(), rgb));
+}
+
+void Light::SetColor(uint32_t variable_id) {
+ session()->Enqueue(NewSetLightColorCmd(id(), variable_id));
+}
+
+void Light::Detach() { session()->Enqueue(NewDetachLightCmd(id())); }
+
+AmbientLight::AmbientLight(Session* session) : Light(session) {
+ session->Enqueue(NewCreateAmbientLightCmd(id()));
+}
+
+AmbientLight::AmbientLight(AmbientLight&& moved) : Light(std::move(moved)) {}
+
+AmbientLight::~AmbientLight() = default;
+
+DirectionalLight::DirectionalLight(Session* session) : Light(session) {
+ session->Enqueue(NewCreateDirectionalLightCmd(id()));
+}
+
+DirectionalLight::DirectionalLight(DirectionalLight&& moved)
+ : Light(std::move(moved)) {}
+
+DirectionalLight::~DirectionalLight() = default;
+
+void DirectionalLight::SetDirection(const float direction[3]) {
+ session()->Enqueue(NewSetLightDirectionCmd(id(), direction));
+}
+
+void DirectionalLight::SetDirection(uint32_t variable_id) {
+ session()->Enqueue(NewSetLightDirectionCmd(id(), variable_id));
+}
+
+} // namespace scenic
diff --git a/pkg/scenic_cpp/session.cc b/pkg/scenic_cpp/session.cc
new file mode 100644
index 0000000..7f48da9
--- /dev/null
+++ b/pkg/scenic_cpp/session.cc
@@ -0,0 +1,184 @@
+// Copyright 2017 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/ui/scenic/cpp/session.h"
+
+#include <stdio.h>
+#include <zircon/assert.h>
+
+#include "lib/ui/scenic/cpp/commands.h"
+
+namespace scenic {
+
+constexpr size_t kCommandsPerMessage =
+ (ZX_CHANNEL_MAX_MSG_BYTES - sizeof(fidl_message_header_t) -
+ sizeof(fidl_vector_t)) /
+ sizeof(fuchsia::ui::scenic::Command);
+
+SessionPtrAndListenerRequest CreateScenicSessionPtrAndListenerRequest(
+ fuchsia::ui::scenic::Scenic* scenic) {
+ fuchsia::ui::scenic::SessionPtr session;
+ fidl::InterfaceHandle<fuchsia::ui::scenic::SessionListener> listener_handle;
+ auto listener_request = listener_handle.NewRequest();
+
+ scenic->CreateSession(session.NewRequest(), listener_handle.Bind());
+
+ return {std::move(session), std::move(listener_request)};
+}
+
+Session::Session(fuchsia::ui::scenic::SessionPtr session,
+ fidl::InterfaceRequest<fuchsia::ui::scenic::SessionListener>
+ session_listener)
+ : session_(std::move(session)), session_listener_binding_(this) {
+ ZX_DEBUG_ASSERT(session_);
+ if (session_listener.is_valid())
+ session_listener_binding_.Bind(std::move(session_listener));
+}
+
+Session::Session(fuchsia::ui::scenic::Scenic* scenic)
+ : session_listener_binding_(this) {
+ ZX_DEBUG_ASSERT(scenic);
+ scenic->CreateSession(session_.NewRequest(),
+ session_listener_binding_.NewBinding());
+}
+
+Session::Session(SessionPtrAndListenerRequest session_and_listener)
+ : Session(std::move(session_and_listener.first),
+ std::move(session_and_listener.second)) {}
+
+Session::~Session() {
+ ZX_DEBUG_ASSERT_MSG(resource_count_ == 0,
+ "Some resources outlived the session: %u",
+ resource_count_);
+}
+
+uint32_t Session::AllocResourceId() {
+ uint32_t resource_id = next_resource_id_++;
+ ZX_DEBUG_ASSERT(resource_id);
+ resource_count_++;
+ return resource_id;
+}
+
+void Session::ReleaseResource(uint32_t resource_id) {
+ resource_count_--;
+ Enqueue(NewReleaseResourceCmd(resource_id));
+}
+
+void Session::Enqueue(fuchsia::ui::gfx::Command command) {
+ Enqueue(NewCommand(std::move(command)));
+}
+
+void Session::Enqueue(fuchsia::ui::input::Command command) {
+ Enqueue(NewCommand(std::move(command)));
+}
+
+void Session::Enqueue(fuchsia::ui::scenic::Command command) {
+ commands_.push_back(std::move(command));
+ if (commands_->size() >= kCommandsPerMessage ||
+ command.Which() == fuchsia::ui::scenic::Command::Tag::kInput) {
+ Flush();
+ }
+}
+
+void Session::EnqueueAcquireFence(zx::event fence) {
+ ZX_DEBUG_ASSERT(fence);
+ acquire_fences_.push_back(std::move(fence));
+}
+
+void Session::EnqueueReleaseFence(zx::event fence) {
+ ZX_DEBUG_ASSERT(fence);
+ release_fences_.push_back(std::move(fence));
+}
+
+void Session::Flush() {
+ ZX_DEBUG_ASSERT(session_);
+ if (!commands_->empty()) {
+ ZX_DEBUG_ASSERT(static_cast<bool>(commands_));
+ session_->Enqueue(std::move(commands_));
+
+ // After being moved, |commands_| is in a "valid but unspecified state";
+ // see http://en.cppreference.com/w/cpp/utility/move. Calling reset() makes
+ // it safe to continue using.
+ commands_.reset();
+ }
+}
+
+void Session::Present(uint64_t presentation_time, PresentCallback callback) {
+ ZX_DEBUG_ASSERT(session_);
+ Flush();
+
+ if (acquire_fences_.is_null())
+ acquire_fences_.resize(0u);
+ if (release_fences_.is_null())
+ release_fences_.resize(0u);
+ session_->Present(presentation_time, std::move(acquire_fences_),
+ std::move(release_fences_), std::move(callback));
+}
+
+void Session::HitTest(uint32_t node_id, const float ray_origin[3],
+ const float ray_direction[3], HitTestCallback callback) {
+ ZX_DEBUG_ASSERT(session_);
+ fuchsia::ui::gfx::vec3 ray_origin_vec;
+ ray_origin_vec.x = ray_origin[0];
+ ray_origin_vec.y = ray_origin[1];
+ ray_origin_vec.z = ray_origin[2];
+
+ fuchsia::ui::gfx::vec3 ray_direction_vec;
+ ray_direction_vec.x = ray_direction[0];
+ ray_direction_vec.y = ray_direction[1];
+ ray_direction_vec.z = ray_direction[2];
+
+ session_->HitTest(node_id, std::move(ray_origin_vec),
+ std::move(ray_direction_vec), std::move(callback));
+}
+
+void Session::HitTestDeviceRay(
+ const float ray_origin[3], const float ray_direction[3],
+ fuchsia::ui::scenic::Session::HitTestDeviceRayCallback callback) {
+ ZX_DEBUG_ASSERT(session_);
+ fuchsia::ui::gfx::vec3 ray_origin_vec;
+ ray_origin_vec.x = ray_origin[0];
+ ray_origin_vec.y = ray_origin[1];
+ ray_origin_vec.z = ray_origin[2];
+
+ fuchsia::ui::gfx::vec3 ray_direction_vec;
+ ray_direction_vec.x = ray_direction[0];
+ ray_direction_vec.y = ray_direction[1];
+ ray_direction_vec.z = ray_direction[2];
+
+ session_->HitTestDeviceRay(std::move(ray_origin_vec),
+ std::move(ray_direction_vec), std::move(callback));
+}
+
+void Session::Unbind() {
+ ZX_DEBUG_ASSERT(session_);
+ ZX_DEBUG_ASSERT(!session_handle_);
+ session_handle_ = session_.Unbind();
+ session_ = nullptr;
+}
+
+void Session::Rebind() {
+ ZX_DEBUG_ASSERT(!session_);
+ ZX_DEBUG_ASSERT(session_handle_);
+ session_ = fuchsia::ui::scenic::SessionPtr(session_handle_.Bind());
+ session_handle_ = nullptr;
+}
+
+void Session::OnScenicError(fidl::StringPtr error) {
+ // TODO(SCN-903): replace fprintf with SDK-approved logging mechanism. Also
+ // remove "#include <stdio.h>".
+ fprintf(stderr, "Session error: %s", error->c_str());
+}
+
+void Session::OnScenicEvent(
+ fidl::VectorPtr<fuchsia::ui::scenic::Event> events) {
+ if (event_handler_)
+ event_handler_(std::move(events));
+}
+
+void Session::SetDebugName(const std::string& debug_name) {
+ session_->SetDebugName(debug_name);
+}
+
+} // namespace scenic
diff --git a/pkg/svc/include/lib/svc/dir.h b/pkg/svc/include/lib/svc/dir.h
new file mode 100644
index 0000000..8e16c97
--- /dev/null
+++ b/pkg/svc/include/lib/svc/dir.h
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef LIB_SVC_SVC_H_
+#define LIB_SVC_SVC_H_
+
+#include <lib/async/dispatcher.h>
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+typedef void(svc_connector_t)(void* context, const char* service_name,
+ zx_handle_t service_request);
+
+typedef struct svc_dir svc_dir_t;
+
+__EXPORT zx_status_t svc_dir_create(async_dispatcher_t* dispatcher,
+ zx_handle_t directory_request,
+ svc_dir_t** out_result);
+
+// Adds a service named |service_name| to the given |dir|.
+//
+// If |type| is non-NULL, the service will be published in a directory whose
+// name matches the |type|. If |type| is NULL, the service will be published in
+// the root directory.
+//
+// The most commonly used values for |type| are "public", "debug", and "ctrl".
+// Services published under "public" are made available to clients via
+// |fuchsia.sys.Lancher#CreateComponent|. The "debug" serivices are exposed via
+// the hub. The "ctrl" services are used by the core platform to communicate
+// with your program.
+//
+// When a client requests the service, |handler| will be called on the async_t
+// passed to |svc_dir_create|. The |context| will be passed to |handler| as its
+// first argument.
+//
+// This may fail in two ways. If an entry with the given
+// |service_name| already exists, this returns
+// ZX_ERR_ALREADY_EXISTS. If the provided |service_name| is invalid,
+// ZX_ERR_INVALID_ARGS is returned. Otherwise, this returns ZX_OK.
+__EXPORT zx_status_t svc_dir_add_service(svc_dir_t* dir, const char* type,
+ const char* service_name,
+ void* context,
+ svc_connector_t* handler);
+
+// Removes the service named |service_name| of type |type| from the
+// given |dir|. This reports a failure if the entry does not exist, by
+// returning ZX_ERR_NOT_FOUND. Otherwise, the service entry is
+// removed, and ZX_OK is returned.
+__EXPORT zx_status_t svc_dir_remove_service(svc_dir_t* dir, const char* type,
+ const char* service_name);
+
+// Destroy the provided directory. This currently cannot fail, and
+// returns ZX_OK.
+__EXPORT zx_status_t svc_dir_destroy(svc_dir_t* dir);
+
+__END_CDECLS
+
+#endif // LIB_SVC_SVC_H_
diff --git a/pkg/svc/meta.json b/pkg/svc/meta.json
new file mode 100644
index 0000000..2a183d5
--- /dev/null
+++ b/pkg/svc/meta.json
@@ -0,0 +1,27 @@
+{
+ "binaries": {
+ "arm64": {
+ "debug": ".build-id/28/16547647b2a89d.debug",
+ "dist": "arch/arm64/dist/libsvc.so",
+ "link": "arch/arm64/lib/libsvc.so"
+ },
+ "x64": {
+ "debug": ".build-id/31/f3907157249ab0.debug",
+ "dist": "arch/x64/dist/libsvc.so",
+ "link": "arch/x64/lib/libsvc.so"
+ }
+ },
+ "deps": [
+ "async",
+ "fdio",
+ "trace-engine"
+ ],
+ "format": "shared",
+ "headers": [
+ "pkg/svc/include/lib/svc/dir.h"
+ ],
+ "include_dir": "pkg/svc/include",
+ "name": "svc",
+ "root": "pkg/svc",
+ "type": "cc_prebuilt_library"
+}
\ No newline at end of file
diff --git a/pkg/sync/include/lib/sync/completion.h b/pkg/sync/include/lib/sync/completion.h
new file mode 100644
index 0000000..515a563
--- /dev/null
+++ b/pkg/sync/include/lib/sync/completion.h
@@ -0,0 +1,55 @@
+// Copyright 2016 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.
+
+#ifndef LIB_SYNC_COMPLETION_H_
+#define LIB_SYNC_COMPLETION_H_
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+typedef struct sync_completion {
+ zx_futex_t futex;
+
+#ifdef __cplusplus
+ sync_completion()
+ : futex(0) {}
+#endif
+} sync_completion_t;
+
+#if !defined(__cplusplus)
+#define SYNC_COMPLETION_INIT ((sync_completion_t){0})
+#endif
+
+// Returns ZX_ERR_TIMED_OUT if timeout elapses, and ZX_OK if woken by
+// a call to sync_completion_signal or if the completion has already been
+// signaled.
+zx_status_t sync_completion_wait(sync_completion_t* completion, zx_duration_t timeout);
+
+// Returns ZX_ERR_TIMED_OUT if deadline elapses, and ZX_OK if woken by
+// a call to sync_completion_signal or if the completion has already been
+// signaled.
+zx_status_t sync_completion_wait_deadline(sync_completion_t* completion, zx_time_t deadline);
+
+// Awakens all waiters on the completion, and marks it as
+// signaled. Waits after this call but before a reset of the
+// completion will also see the signal and immediately return.
+void sync_completion_signal(sync_completion_t* completion);
+
+// Marks the completion as signaled, but doesn't awaken all waiters
+// right away. Instead, all waiters are requeued to the |futex|.
+// Waits after this call but before a reset of the
+// completion will also see the signal and immediately return.
+//
+// Intended to be used by libsync internally, e.g. the condition variable
+// implementation.
+void sync_completion_signal_requeue(sync_completion_t* completion, zx_futex_t* futex);
+
+// Resets the completion's signaled state to unsignaled.
+void sync_completion_reset(sync_completion_t* completion);
+
+__END_CDECLS
+
+#endif // LIB_SYNC_COMPLETION_H_
diff --git a/pkg/sync/include/lib/sync/condition.h b/pkg/sync/include/lib/sync/condition.h
new file mode 100644
index 0000000..6f6eb58
--- /dev/null
+++ b/pkg/sync/include/lib/sync/condition.h
@@ -0,0 +1,95 @@
+// 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.
+
+#ifndef LIB_SYNC_CONDITION_H_
+#define LIB_SYNC_CONDITION_H_
+
+#include <lib/sync/mutex.h>
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+// A condition variable that works with a sync_mutex_t
+typedef struct sync_condition {
+ int lock;
+ void* head;
+ void* tail;
+
+#ifdef __cplusplus
+ sync_condition()
+ : lock(0), head(nullptr), tail(nullptr) {}
+#endif
+} sync_condition_t;
+
+#if !defined(__cplusplus)
+#define SYNC_CONDITION_INIT ((sync_condition_t){0})
+#endif
+
+// Block until |condition| is signaled by sync_condition_signal()/sync_condition_broadcast(), or a spurious
+// wake up occurs.
+//
+// |mutex| must be in a locked state, and will be atomically unlocked for the duration of the wait,
+// then locked again before the function returns.
+void sync_condition_wait(sync_condition_t* condition, sync_mutex_t* mutex);
+
+// Block until |condition| is signaled by sync_condition_signal()/sync_condition_broadcast(), or a spurious
+// wake up or a timeout occurs.
+//
+// |mutex| must be in a locked state, and will be atomically unlocked for the duration of the wait,
+// then locked again before the function returns.
+//
+// ZX_TIME_INFINITE can be used for |deadline| to wait for an unlimited amount of time.
+//
+// Return value:
+// ZX_OK if |condition| was signaled or a spurious wake up occurred.
+// ZX_ERR_TIMED_OUT if the wait timed out.
+zx_status_t sync_condition_timedwait(sync_condition_t* condition, sync_mutex_t* mutex, zx_time_t deadline);
+
+// Wake up one thread waiting for |condition|.
+//
+// If the woken thread was waiting on sync_condition_timedwait(), then it is guaranteed
+// to receive a ZX_OK return value even if a race with a timeout occurs. As an example
+// where this makes a difference, consider the following implementation of a multi-producer,
+// multi-consumer queue:
+//
+// Message* MessageQueue::DequeueTimeout(zx_time_t deadline) {
+// sync_mutex_lock(&mutex_);
+// for (;;) {
+// if (!list_.empty()) {
+// Message* msg = list_.front();
+// list_.pop_front();
+// sync_mutex_unlock(&mutex_);
+// return msg;
+// }
+// zx_status_t status = sync_condition_timedwait(&condition_, &mutex_, deadline);
+// if (status == ZX_ERR_TIMED_OUT) {
+// // Without the above guarantee, this would be a bug: a race between
+// // a timeout and a signal() would result in a missed wakeup.
+// // To fix that, we would need to recheck list_.empty() here, which
+// // is not obvious and would make the code more complex.
+// sync_mutex_unlock(&mutex_);
+// return nullptr;
+// }
+// }
+// }
+//
+// void MessageQueue::Enqueue(Message* msg) {
+// sync_mutex_lock(&mutex_);
+// list_.push_back(msg);
+// // Signal just one waiter. Assumes that any possible waiter will dequeue the message.
+// sync_condition_signal(&condvar_);
+// sync_mutex_unlock(&mutex_);
+// }
+//
+// Note that pthread does not seem to require this property, and in fact the current upstream
+// implementation of pthread_cond_timedwait() in MUSL does not have it.
+void sync_condition_signal(sync_condition_t* condition);
+
+// Wake up all threads that are currently waiting for |condition|.
+void sync_condition_broadcast(sync_condition_t* condition);
+
+__END_CDECLS
+
+#endif // LIB_SYNC_CONDITION_H_
diff --git a/pkg/sync/include/lib/sync/internal/condition-template.h b/pkg/sync/include/lib/sync/internal/condition-template.h
new file mode 100644
index 0000000..7bd4f2d
--- /dev/null
+++ b/pkg/sync/include/lib/sync/internal/condition-template.h
@@ -0,0 +1,296 @@
+// 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.
+
+#ifndef LIB_SYNC_INTERNAL_CONDITION_TEMPLATE_H_
+#define LIB_SYNC_INTERNAL_CONDITION_TEMPLATE_H_
+
+#include <zircon/syscalls.h>
+#include <lib/sync/completion.h>
+#include <lib/sync/mutex.h>
+
+namespace condition_impl_internal {
+
+// A template implementation of a condition variable.
+// The algorithm is borrowed from MUSL.
+//
+// The 'Condition' struct must contain the following fields:
+// int lock;
+// void* head;
+// void* tail;
+//
+// The following struct template must be specialized for the mutex type 'Mutex'
+// in order to instantiate the template:
+template <typename Mutex>
+struct MutexOps {
+ // Return a pointer to the futex that backs the |mutex|
+ static zx_futex_t* get_futex(Mutex* mutex);
+
+ // Lock the |mutex|. If an error occurs while locking the mutex,
+ // ZX_ERR_BAD_STATE must be returned. An implementation-defined
+ // error code can be returned via |mutex_lock_err| if it's not null.
+ static zx_status_t lock(Mutex* mutex, int* mutex_lock_err);
+
+ // Similar to lock(), but also update the waiter information in the mutex.
+ // If the mutex implements waiter counting, then the count must be adjusted
+ // by |waiters_delta|. Otherwise, the mutex must be marked as potentially
+ // having waiters.
+ static zx_status_t lock_with_waiters(
+ Mutex* mutex, int waiters_delta, int* mutex_lock_err);
+
+ // Unlock the mutex
+ static void unlock(Mutex* mutex);
+};
+
+// Note that this library is used by libc, and as such needs to use
+// '_zx_' function names for syscalls and not the regular 'zx_' names.
+
+static inline void spin() {
+#if defined(__x86_64__)
+ __asm__ __volatile__("pause"
+ :
+ :
+ : "memory");
+#elif defined(__aarch64__)
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
+#else
+#error Please define spin() for your architecture
+#endif
+}
+
+static inline bool cas(int* ptr, int* expected, int desired) {
+ return __atomic_compare_exchange_n(ptr, expected, desired, false,
+ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+}
+
+static inline void wait(int* futex, int current_value) {
+ int spins = 100;
+ while (spins--) {
+ if (__atomic_load_n(futex, __ATOMIC_SEQ_CST) == current_value) {
+ spin();
+ } else {
+ return;
+ }
+ }
+ while (__atomic_load_n(futex, __ATOMIC_SEQ_CST) == current_value) {
+ _zx_futex_wait(futex, current_value, ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
+ }
+}
+
+enum {
+ WAITING,
+ SIGNALED,
+ LEAVING,
+};
+
+struct Waiter {
+ Waiter* prev = nullptr;
+ Waiter* next = nullptr;
+ int state = WAITING;
+ sync_completion_t ready;
+ int* notify = nullptr;
+};
+
+// Return value:
+// - ZX_OK if the condition variable was signaled;
+// - ZX_ERR_TIMED_OUT if deadline was reached;
+// - ZX_ERR_BAD_STATE if there was an error locking the mutex.
+// In this case, |mutex_lock_err|, if not null, will be populated with an error code
+// provided by the mutex implementation.
+template <typename Condition, typename Mutex>
+static inline zx_status_t timedwait(Condition* c, Mutex* mutex, zx_time_t deadline,
+ int* mutex_lock_err)
+ __TA_NO_THREAD_SAFETY_ANALYSIS {
+ sync_mutex_lock(reinterpret_cast<sync_mutex_t*>(&c->lock));
+
+ Waiter node;
+
+ // Add our waiter node onto the condition's list. We add the node to the
+ // head of the list, but this is logically the end of the queue.
+ node.next = static_cast<Waiter*>(c->head);
+ c->head = &node;
+ if (!c->tail) {
+ c->tail = &node;
+ } else {
+ node.next->prev = &node;
+ }
+
+ sync_mutex_unlock(reinterpret_cast<sync_mutex_t*>(&c->lock));
+
+ MutexOps<Mutex>::unlock(mutex);
+
+ // Wait to be signaled. There are multiple ways this wait could finish:
+ // 1) After being woken by signal().
+ // 2) After being woken by a mutex unlock, after we were
+ // requeued from the condition's futex to the mutex's futex (by
+ // timedwait() in another thread).
+ // 3) After a timeout.
+ // In the original Linux version of this algorithm, this could also exit
+ // when interrupted by an asynchronous signal, but that does not apply on Zircon.
+ sync_completion_wait_deadline(&node.ready, deadline);
+
+ int oldstate = WAITING;
+ if (cas(&node.state, &oldstate, LEAVING)) {
+ // The wait timed out. So far, this thread was not signaled by
+ // signal() -- this thread was able to move state.node out of the
+ // WAITING state before any signal() call could do that.
+ //
+ // This thread must therefore remove the waiter node from the
+ // list itself.
+
+ // Access to cv object is valid because this waiter was not
+ // yet signaled and a new signal/broadcast cannot return
+ // after seeing a LEAVING waiter without getting notified
+ // via the futex notify below.
+
+ sync_mutex_lock(reinterpret_cast<sync_mutex_t*>(&c->lock));
+
+ // Remove our waiter node from the list.
+ if (c->head == &node) {
+ c->head = node.next;
+ } else if (node.prev) {
+ node.prev->next = node.next;
+ }
+
+ if (c->tail == &node) {
+ c->tail = node.prev;
+ } else if (node.next) {
+ node.next->prev = node.prev;
+ }
+
+ sync_mutex_unlock(reinterpret_cast<sync_mutex_t*>(&c->lock));
+
+ // It is possible that signal() saw our waiter node after we set
+ // node.state to LEAVING but before we removed the node from the
+ // list. If so, it will have set node.notify and will be waiting
+ // on it, and we need to wake it up.
+ //
+ // This is rather complex. An alternative would be to eliminate
+ // the |node.state| field and always claim |lock| if we could have
+ // got a timeout. However, that presumably has higher overhead
+ // (since it contends |lock| and involves more atomic ops).
+ if (node.notify) {
+ if (__atomic_fetch_add(node.notify, -1, __ATOMIC_SEQ_CST) == 1) {
+ _zx_futex_wake(node.notify, 1);
+ }
+ }
+
+ // We don't need lock_with_waiters() here: we haven't been signaled, and will
+ // never be since we managed to claim the state as LEAVING. This means that
+ // we could not have been woken up by unlock_requeue() + mutex unlock().
+ if (MutexOps<Mutex>::lock(mutex, mutex_lock_err) != ZX_OK) {
+ return ZX_ERR_BAD_STATE;
+ }
+ return ZX_ERR_TIMED_OUT;
+ }
+
+ // Since the CAS above failed, we have been signaled.
+ // It could still be the case that sync_completion_wait_deadline() above timed out,
+ // so we need to make sure to wait for the completion to control the wake order.
+ // If the completion has already been signaled, this will return immediately.
+ sync_completion_wait_deadline(&node.ready, ZX_TIME_INFINITE);
+
+ // By this point, our part of the waiter list cannot change further.
+ // It has been unlinked from the condition by signal().
+ // Any timed out waiters would have removed themselves from the list
+ // before signal() signaled the first node.ready in our list.
+ //
+ // It is therefore safe now to read node.next and node.prev without
+ // holding c->lock.
+
+ // As an optimization, we only update waiter count at the beginning and
+ // end of the signaled list.
+ int waiters_delta = 0;
+ if (!node.prev) {
+ waiters_delta++;
+ }
+ if (!node.next) {
+ waiters_delta--;
+ }
+
+ // We must leave the mutex in the "locked with waiters" state here
+ // (or adjust its waiter count, depending on the implementation).
+ // There are two reasons for that:
+ // 1) If we do the unlock_requeue() below, a condition waiter will be
+ // requeued to the mutex's futex. We need to ensure that it will
+ // be signaled by mutex unlock() in future.
+ // 2) If the current thread was woken via an unlock_requeue() +
+ // mutex unlock, there *might* be another thread waiting for
+ // the mutex after us in the queue. We need to ensure that it
+ // will be signaled by zxr_mutex_unlock() in future.
+ zx_status_t status = MutexOps<Mutex>::lock_with_waiters(mutex, waiters_delta, mutex_lock_err);
+
+ if (node.prev) {
+ // Signal the completion that's holding back the next waiter, and
+ // requeue it to the mutex so that it will be woken when the
+ // mutex is unlocked.
+ sync_completion_signal_requeue(&node.prev->ready, MutexOps<Mutex>::get_futex(mutex));
+ }
+
+ // Even if the first call to sync_completion_wait_deadline() timed out,
+ // we still have been signaled. Thus we still return ZX_OK rather than
+ // ZX_ERR_TIMED_OUT. This provides the following guarantee: if multiple
+ // threads are waiting when signal() is called, at least one waiting
+ // thread will be woken *and* get a ZX_OK from timedwait() (unless there
+ // is an error locking the mutex). This property does not appear to be
+ // required by pthread condvars, although an analogous property is
+ // required for futex wake-ups. We also require this property for
+ // sync_condition_t.
+ return status;
+}
+
+// This will wake up to |n| threads that are waiting on the condition,
+// or all waiting threads if |n| is set to -1
+template <typename Condition>
+static inline void signal(Condition* c, int n) {
+ Waiter* p;
+ Waiter* first = nullptr;
+ int ref = 0;
+ int cur;
+
+ sync_mutex_lock(reinterpret_cast<sync_mutex_t*>(&c->lock));
+ for (p = static_cast<Waiter*>(c->tail); n && p; p = p->prev) {
+ int oldstate = WAITING;
+ if (!cas(&p->state, &oldstate, SIGNALED)) {
+ // This waiter timed out, and it marked itself as in the
+ // LEAVING state. However, it hasn't yet claimed |lock|
+ // (since we claimed the lock first) and so it hasn't yet
+ // removed itself from the list. We will wait for the waiter
+ // to remove itself from the list and to notify us of that.
+ __atomic_fetch_add(&ref, 1, __ATOMIC_SEQ_CST);
+ p->notify = &ref;
+ } else {
+ n--;
+ if (!first) {
+ first = p;
+ }
+ }
+ }
+ // Split the list, leaving any remainder on the cv.
+ if (p) {
+ if (p->next) {
+ p->next->prev = 0;
+ }
+ p->next = 0;
+ } else {
+ c->head = 0;
+ }
+ c->tail = p;
+ sync_mutex_unlock(reinterpret_cast<sync_mutex_t*>(&c->lock));
+
+ // Wait for any waiters in the LEAVING state to remove
+ // themselves from the list before returning or allowing
+ // signaled threads to proceed.
+ while ((cur = __atomic_load_n(&ref, __ATOMIC_SEQ_CST))) {
+ wait(&ref, cur);
+ }
+
+ // Allow first signaled waiter, if any, to proceed.
+ if (first) {
+ sync_completion_signal(&first->ready);
+ }
+}
+
+} // namespace condition_impl_internal
+
+#endif // LIB_SYNC_INTERNAL_CONDITION_TEMPLATE_H_
diff --git a/pkg/sync/include/lib/sync/mutex.h b/pkg/sync/include/lib/sync/mutex.h
new file mode 100644
index 0000000..37a5cd3
--- /dev/null
+++ b/pkg/sync/include/lib/sync/mutex.h
@@ -0,0 +1,72 @@
+// 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.
+
+#ifndef LIB_SYNC_MUTEX_H_
+#define LIB_SYNC_MUTEX_H_
+
+#include <zircon/compiler.h>
+#include <zircon/types.h>
+
+__BEGIN_CDECLS
+
+// An optimal, non-recursive mutex on Fuchsia.
+//
+// The |mutex_t| mutex in the standard library has several quirks in its design
+// that prevent it from being optimal. For example, the |mutex_t| interface
+// supports recursion, which adds a branch to |mutex_init| to check that the
+// client has not asked for recusion, and |mutex_timedlock| operates in
+// |struct timespec| rather than |zx_time_t|.
+//
+// |sync_mutex| resolves these issues.
+typedef struct __TA_CAPABILITY("mutex") sync_mutex {
+ zx_futex_t futex;
+
+#ifdef __cplusplus
+ sync_mutex()
+ : futex(0) {}
+#endif
+} sync_mutex_t;
+
+#if !defined(__cplusplus)
+#define SYNC_MUTEX_INIT ((sync_mutex_t){0})
+#endif
+
+// Locks the mutex.
+//
+// The current thread will block until the mutex is acquired. The mutex is
+// non-recursive, which means attempting to lock a mutex that is already held by
+// this thread will deadlock.
+void sync_mutex_lock(sync_mutex_t* mutex) __TA_ACQUIRE(mutex);
+
+// Locks the mutex and mark the mutex as having a waiter.
+//
+// Similar to |sync_mutex_lock| but markes the mutex as having a waiter. Intended
+// to be used by the condition variable implementation.
+void sync_mutex_lock_with_waiter(sync_mutex_t* mutex) __TA_ACQUIRE(mutex);
+
+// Attempt to lock the mutex until |deadline|.
+//
+// The current thread will block until either the mutex is acquired or
+// |deadline| passes.
+//
+// |deadline| is expressed as an absolute time in the ZX_CLOCK_MONOTONIC
+// timebase.
+//
+// Returns |ZX_OK| if the lock is acquired, and |ZX_ERR_TIMED_OUT| if the
+// deadline passes.
+zx_status_t sync_mutex_timedlock(sync_mutex_t* mutex, zx_time_t deadline);
+
+// Attempts to lock the mutex without blocking.
+//
+// Returns |ZX_OK| if the lock is obtained, and |ZX_ERR_BAD_STATE| if not.
+zx_status_t sync_mutex_trylock(sync_mutex_t* mutex);
+
+// Unlocks the mutex.
+//
+// Does nothing if the mutex is already unlocked.
+void sync_mutex_unlock(sync_mutex_t* mutex) __TA_RELEASE(mutex);
+
+__END_CDECLS
+
+#endif // LIB_SYNC_MUTEX_H_
diff --git a/pkg/sync/meta.json b/pkg/sync/meta.json
new file mode 100644
index 0000000..4908432
--- /dev/null
+++ b/pkg/sync/meta.json
@@ -0,0 +1,22 @@
+{
+ "binaries": {
+ "arm64": {
+ "link": "arch/arm64/lib/libsync.a"
+ },
+ "x64": {
+ "link": "arch/x64/lib/libsync.a"
+ }
+ },
+ "deps": [],
+ "format": "static",
+ "headers": [
+ "pkg/sync/include/lib/sync/completion.h",
+ "pkg/sync/include/lib/sync/condition.h",
+ "pkg/sync/include/lib/sync/internal/condition-template.h",
+ "pkg/sync/include/lib/sync/mutex.h"
+ ],
+ "include_dir": "pkg/sync/include",
+ "name": "sync",
+ "root": "pkg/sync",
+ "type": "cc_prebuilt_library"
+}
\ No newline at end of file
diff --git a/pkg/syslog/include/lib/syslog/global.h b/pkg/syslog/include/lib/syslog/global.h
new file mode 100644
index 0000000..9c9cc89
--- /dev/null
+++ b/pkg/syslog/include/lib/syslog/global.h
@@ -0,0 +1,132 @@
+// 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.
+
+//
+// Entry points used by clients.
+
+#ifndef LIB_SYSLOG_GLOBAL_H_
+#define LIB_SYSLOG_GLOBAL_H_
+
+#include <lib/syslog/logger.h>
+
+__BEGIN_CDECLS
+
+// Gets the global logger for the process to which log messages emitted
+// using the FX_LOG macros will be written. Returns NULL if logging is
+// not configured.
+fx_logger_t* fx_log_get_logger(void);
+
+// Returns true if writing messages with the given severity is enabled in the
+// global logger.
+static inline bool fx_log_is_enabled(fx_log_severity_t severity) {
+ fx_logger_t* logger = fx_log_get_logger();
+ return logger && severity >= fx_logger_get_min_severity(logger);
+}
+
+// Initializes the logging infrastructure with the specified configuration.
+// Returns |ZX_ERR_BAD_STATE| if logging has already been initialized.
+// If |console_fd| and |log_service_channel| are invalid in |config|,
+// this function chooses a default destination for the log.
+// |config| can be safely deallocated after this function returns.
+//
+// global logger would be deallocated once program ends.
+zx_status_t fx_log_init_with_config(const fx_logger_config_t* config);
+
+// Initializes the logging infrastructure for this process using default
+// parameters. Returns |ZX_ERR_BAD_STATE| if logging has already been
+// initialized.
+//
+// global logger would be deallocated once program ends.
+zx_status_t fx_log_init(void);
+
+// Returns true if writing messages with the given severity is enabled in the
+// global logger. |severity| is one of DEBUG, INFO, WARNING, ERROR, or FATAL.
+#define FX_LOG_IS_ENABLED(severity) (fx_log_is_enabled(FX_LOG_##severity))
+
+// Returns true if writing messages with the given verbosity is enabled in the
+// global logger. |verbosity| is positive number.
+#define FX_VLOG_IS_ENABLED(verbosity) (fx_log_is_enabled(-(verbosity)))
+
+#define _FX_LOG_SET_SEVERITY(severity) \
+ do { \
+ fx_logger_t* logger = fx_log_get_logger(); \
+ if (logger) { \
+ fx_logger_set_min_severity(logger, (severity)); \
+ } \
+ } while (0)
+
+// Sets severity for global logger.
+// |severity| is one of DEBUG, INFO, WARNING, ERROR, or FATAL.
+#define FX_LOG_SET_SEVERITY(severity) _FX_LOG_SET_SEVERITY(FX_LOG_##severity)
+
+// Sets verbosity for global logger.
+// |verbosity| is positive number. Logger severity is set to -verbosity
+#define FX_LOG_SET_VERBOSITY(verbosity) _FX_LOG_SET_SEVERITY(-(verbosity))
+
+#define _FX_LOG(severity, tag, message) \
+ do { \
+ fx_logger_t* logger = fx_log_get_logger(); \
+ if (logger && fx_logger_get_min_severity(logger) <= (severity)) { \
+ fx_logger_log(logger, (severity), (tag), (message)); \
+ } \
+ } while (0)
+
+#define _FX_LOGF(severity, tag, message, ...) \
+ do { \
+ fx_logger_t* logger = fx_log_get_logger(); \
+ if (logger && fx_logger_get_min_severity(logger) <= (severity)) { \
+ fx_logger_logf(logger, (severity), (tag), (message), \
+ __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define _FX_LOGVF(severity, tag, message, args) \
+ do { \
+ fx_logger_t* logger = fx_log_get_logger(); \
+ if (logger && fx_logger_get_min_severity(logger) <= (severity)) { \
+ fx_logger_logvf(logger, (severity), (tag), (message), (args)); \
+ } \
+ } while (0)
+
+// Writes a message to the global logger.
+// |severity| is one of DEBUG, INFO, WARNING, ERROR, FATAL
+// |tag| is a tag to associated with the message, or NULL if none.
+// |message| is the message to write, or NULL if none.
+#define FX_LOG(severity, tag, message) _FX_LOG((FX_LOG_##severity), tag, message)
+
+// Writes formatted message to the global logger.
+// |severity| is one of DEBUG, INFO, WARNING, ERROR, FATAL
+// |tag| is a tag to associated with the message, or NULL if none.
+// |message| is the message to write, or NULL if none.
+#define FX_LOGF(severity, tag, message, ...) _FX_LOGF((FX_LOG_##severity), tag, message, __VA_ARGS__)
+
+// Writes formatted message to the global logger using vaargs
+// |severity| is one of DEBUG, INFO, WARNING, ERROR, FATAL
+// |tag| is a tag to associated with the message, or NULL if none.
+// |message| is the message to write, or NULL if none.
+// |args| are the arguments to |message|.
+#define FX_LOGVF(severity, tag, message, args) _FX_LOGVF((FX_LOG_##severity), tag, message, args)
+
+// Writes verbose message to the global logger.
+// |verbosity| is positive integer.
+// |tag| is a tag to associated with the message, or NULL if none.
+// |message| is the message to write, or NULL if none.
+#define FX_VLOG(verbosity, tag, message) _FX_LOG(-(verbosity), tag, message)
+
+// Writes formatted verbose message to the global logger.
+// |verbosity| is positive integer.
+// |tag| is a tag to associated with the message, or NULL if none.
+// |message| is the message to write, or NULL if none.
+#define FX_VLOGF(verbosity, tag, message, ...) _FX_LOGF(-(verbosity), tag, message, __VA_ARGS__)
+
+// Writes formatted verbose message to the global logger using vaargs
+// |verbosity| is positive integer.
+// |tag| is a tag to associated with the message, or NULL if none.
+// |message| is the message to write, or NULL if none.
+// |args| are the arguments to |message|.
+#define FX_VLOGVF(verbosity, tag, message, args) _FX_LOGVF(-(verbosity), tag, message, args)
+
+__END_CDECLS
+
+#endif // LIB_SYSLOG_GLOBAL_H_
diff --git a/pkg/syslog/include/lib/syslog/logger.h b/pkg/syslog/include/lib/syslog/logger.h
new file mode 100644
index 0000000..26e001a
--- /dev/null
+++ b/pkg/syslog/include/lib/syslog/logger.h
@@ -0,0 +1,126 @@
+// 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.
+
+//
+// This header contains definition for the logger object and protocol.
+
+#ifndef LIB_SYSLOG_LOGGER_H_
+#define LIB_SYSLOG_LOGGER_H_
+
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <zircon/types.h>
+
+// Max no of tags associated with a logger.
+#define FX_LOG_MAX_TAGS (4)
+
+// Max individual tag length including terminating character.
+#define FX_LOG_MAX_TAG_LEN (64)
+
+// Log entry severity.
+// Used for coarse filtering of log messages
+typedef int fx_log_severity_t;
+#define FX_LOG_INFO (0)
+#define FX_LOG_WARNING (1)
+#define FX_LOG_ERROR (2)
+#define FX_LOG_FATAL (3)
+
+__BEGIN_CDECLS
+
+// Configuration for a logger object.
+// Specifies the destination to which log messages should be written.
+// Multiple destinations may be used concurrently.
+typedef struct fx_logger_config {
+ // The minimum log severity.
+ // Log messages with lower severity will be discarded.
+ fx_log_severity_t min_severity;
+
+ // The file descriptor to which formatted log messages should be written,
+ // or -1 if log messages should not be written to the console.
+ // logger takes ownership of this fd.
+ int console_fd;
+
+ // The FIDL log service channel to which the logger should connect, or
+ // |ZX_HANDLE_INVALID| if the logger should not connect to the log service.
+ // logger takes ownership of this handle.
+ zx_handle_t log_service_channel;
+
+ // An array of tag strings to associate with all messages written
+ // by this logger. Tags will be truncated if they are (individually) longer
+ // than |FX_LOG_MAX_TAG_LEN|.
+ const char** tags;
+
+ // Number of tag strings. Must be no more than |FX_LOG_MAX_TAGS|.
+ size_t num_tags;
+} fx_logger_config_t;
+
+// Opaque type representing a logger object.
+typedef struct fx_logger fx_logger_t;
+
+// Creates a logger object from the specified configuration.
+//
+// This will return ZX_ERR_INVALID_ARGS if |num_tags| is more than
+// |FX_LOG_MAX_TAGS| and return |ZX_ERR_INTERNAL} if dup fails.
+// |config| can be safely deleted after this function returns.
+zx_status_t fx_logger_create(const fx_logger_config_t* config,
+ fx_logger_t** out_logger);
+
+// Destroys a logger object.
+//
+// This closes |console_fd| or |log_service_channel| which were passed in
+// |fx_logger_config_t|.
+void fx_logger_destroy(fx_logger_t* logger);
+
+// Gets the logger's minimum log severity.
+fx_log_severity_t fx_logger_get_min_severity(fx_logger_t* logger);
+
+// Sets logger severity
+void fx_logger_set_min_severity(fx_logger_t* logger,
+ fx_log_severity_t severity);
+
+// Activates fallback mode and logger starts writing to |fallback_fd|.
+// There is no way to revert this action.
+//
+// This function does not take ownership of |fallback_fd| and it should not be
+// closed till this logger object is no longer in use. Logger will log to
+// stderr if -1 is provided.
+//
+// This function is thread unsafe.
+void fx_logger_activate_fallback(fx_logger_t* logger,
+ int fallback_fd);
+
+// Writes formatted message to a logger.
+// The message will be discarded if |severity| is less than the logger's
+// minimum log severity.
+// The |tag| may be NULL, in which case no additional tags are added to the
+// log message.
+// The |tag| will be truncated if it is longer than |FX_LOG_MAX_TAG_LEN|.
+// No message is written if |message| is NULL.
+zx_status_t fx_logger_logf(fx_logger_t* logger, fx_log_severity_t severity,
+ const char* tag, const char* msg, ...);
+
+// Writes formatted message to a logger using varargs.
+// The message will be discarded if |severity| is less than the logger's
+// minimum log severity.
+// The |tag| may be NULL, in which case no additional tags are added to the
+// log message.
+// The |tag| will be truncated if it is longer than |FX_LOG_MAX_TAG_LEN|.
+// No message is written if |message| is NULL.
+zx_status_t fx_logger_logvf(fx_logger_t* logger, fx_log_severity_t severity,
+ const char* tag, const char* msg, va_list args);
+
+// Writes a message to a logger.
+// The message will be discarded if |severity| is less than the logger's
+// minimum log severity.
+// The |tag| may be NULL, in which case no additional tags are added to the
+// log message.
+// The |tag| will be truncated if it is longer than |FX_LOG_MAX_TAG_LEN|.
+// No message is written if |message| is NULL.
+zx_status_t fx_logger_log(fx_logger_t* logger, fx_log_severity_t severity,
+ const char* tag, const char* msg);
+
+__END_CDECLS
+
+#endif // LIB_SYSLOG_LOGGER_H_
diff --git a/pkg/syslog/include/lib/syslog/wire_format.h b/pkg/syslog/include/lib/syslog/wire_format.h
new file mode 100644
index 0000000..33dbff0
--- /dev/null
+++ b/pkg/syslog/include/lib/syslog/wire_format.h
@@ -0,0 +1,38 @@
+// 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.
+//
+// This header file defines wire format to transfer logs to listening service.
+
+#ifndef LIB_SYSLOG_WIRE_FORMAT_H_
+#define LIB_SYSLOG_WIRE_FORMAT_H_
+
+#include <lib/syslog/logger.h>
+#include <zircon/types.h>
+
+// Defines max length for storing log_metadata, tags and msgbuffer.
+// TODO(anmittal): Increase it when zircon sockets are able to support a higher
+// buffer.
+#define FX_LOG_MAX_DATAGRAM_LEN (2032)
+
+typedef struct fx_log_metadata {
+ zx_koid_t pid;
+ zx_koid_t tid;
+ zx_time_t time;
+ fx_log_severity_t severity;
+
+ // Increment this field whenever there is a socket write error and client
+ // drops the log and send it with next log msg.
+ uint32_t dropped_logs;
+} fx_log_metadata_t;
+
+// Packet to transfer over socket.
+typedef struct fx_log_packet {
+ fx_log_metadata_t metadata;
+
+ // Contains concatenated tags and message and a null terminating character at
+ // the end.
+ char data[FX_LOG_MAX_DATAGRAM_LEN - sizeof(fx_log_metadata_t)];
+} fx_log_packet_t;
+
+#endif // LIB_SYSLOG_WIRE_FORMAT_H_
diff --git a/pkg/syslog/meta.json b/pkg/syslog/meta.json
new file mode 100644
index 0000000..e427a70
--- /dev/null
+++ b/pkg/syslog/meta.json
@@ -0,0 +1,27 @@
+{
+ "binaries": {
+ "arm64": {
+ "debug": ".build-id/ab/d169689a6ad6a1155ae97b4155fa92810bfdce.debug",
+ "dist": "arch/arm64/dist/libsyslog.so",
+ "link": "arch/arm64/lib/libsyslog.so"
+ },
+ "x64": {
+ "debug": ".build-id/6c/dab7364d696b10a4fdeb1f6c64eac9c8f707f5.debug",
+ "dist": "arch/x64/dist/libsyslog.so",
+ "link": "arch/x64/lib/libsyslog.so"
+ }
+ },
+ "deps": [
+ "fdio"
+ ],
+ "format": "shared",
+ "headers": [
+ "pkg/syslog/include/lib/syslog/global.h",
+ "pkg/syslog/include/lib/syslog/logger.h",
+ "pkg/syslog/include/lib/syslog/wire_format.h"
+ ],
+ "include_dir": "pkg/syslog/include",
+ "name": "syslog",
+ "root": "pkg/syslog",
+ "type": "cc_prebuilt_library"
+}
\ No newline at end of file
diff --git a/pkg/sysroot/meta.json b/pkg/sysroot/meta.json
new file mode 100644
index 0000000..3219922
--- /dev/null
+++ b/pkg/sysroot/meta.json
@@ -0,0 +1,539 @@
+{
+ "name": "sysroot",
+ "root": "pkg/sysroot",
+ "type": "sysroot",
+ "versions": {
+ "arm64": {
+ "debug_libs": [
+ "arch/arm64/sysroot/debug/libc.so",
+ "arch/arm64/sysroot/debug/libzircon.so"
+ ],
+ "dist_libs": [
+ "arch/arm64/sysroot/dist/lib/ld.so.1"
+ ],
+ "headers": [
+ "arch/arm64/sysroot/include/alloca.h",
+ "arch/arm64/sysroot/include/ar.h",
+ "arch/arm64/sysroot/include/arpa/ftp.h",
+ "arch/arm64/sysroot/include/arpa/inet.h",
+ "arch/arm64/sysroot/include/arpa/nameser.h",
+ "arch/arm64/sysroot/include/arpa/nameser_compat.h",
+ "arch/arm64/sysroot/include/arpa/telnet.h",
+ "arch/arm64/sysroot/include/arpa/tftp.h",
+ "arch/arm64/sysroot/include/assert.h",
+ "arch/arm64/sysroot/include/bits/aarch64/endian.h",
+ "arch/arm64/sysroot/include/bits/aarch64/fenv.h",
+ "arch/arm64/sysroot/include/bits/aarch64/float.h",
+ "arch/arm64/sysroot/include/bits/aarch64/io.h",
+ "arch/arm64/sysroot/include/bits/aarch64/ioctl.h",
+ "arch/arm64/sysroot/include/bits/aarch64/ipc.h",
+ "arch/arm64/sysroot/include/bits/aarch64/reg.h",
+ "arch/arm64/sysroot/include/bits/aarch64/setjmp.h",
+ "arch/arm64/sysroot/include/bits/aarch64/signal.h",
+ "arch/arm64/sysroot/include/bits/aarch64/stat.h",
+ "arch/arm64/sysroot/include/bits/alltypes.h",
+ "arch/arm64/sysroot/include/bits/endian.h",
+ "arch/arm64/sysroot/include/bits/errno.h",
+ "arch/arm64/sysroot/include/bits/fcntl.h",
+ "arch/arm64/sysroot/include/bits/fenv.h",
+ "arch/arm64/sysroot/include/bits/float.h",
+ "arch/arm64/sysroot/include/bits/io.h",
+ "arch/arm64/sysroot/include/bits/ioctl.h",
+ "arch/arm64/sysroot/include/bits/ipc.h",
+ "arch/arm64/sysroot/include/bits/limits.h",
+ "arch/arm64/sysroot/include/bits/msg.h",
+ "arch/arm64/sysroot/include/bits/null.h",
+ "arch/arm64/sysroot/include/bits/poll.h",
+ "arch/arm64/sysroot/include/bits/posix.h",
+ "arch/arm64/sysroot/include/bits/reg.h",
+ "arch/arm64/sysroot/include/bits/resource.h",
+ "arch/arm64/sysroot/include/bits/sem.h",
+ "arch/arm64/sysroot/include/bits/setjmp.h",
+ "arch/arm64/sysroot/include/bits/shm.h",
+ "arch/arm64/sysroot/include/bits/signal.h",
+ "arch/arm64/sysroot/include/bits/socket.h",
+ "arch/arm64/sysroot/include/bits/stat.h",
+ "arch/arm64/sysroot/include/bits/statfs.h",
+ "arch/arm64/sysroot/include/bits/termios.h",
+ "arch/arm64/sysroot/include/bits/x86_64/endian.h",
+ "arch/arm64/sysroot/include/bits/x86_64/fenv.h",
+ "arch/arm64/sysroot/include/bits/x86_64/float.h",
+ "arch/arm64/sysroot/include/bits/x86_64/io.h",
+ "arch/arm64/sysroot/include/bits/x86_64/ioctl.h",
+ "arch/arm64/sysroot/include/bits/x86_64/ipc.h",
+ "arch/arm64/sysroot/include/bits/x86_64/reg.h",
+ "arch/arm64/sysroot/include/bits/x86_64/setjmp.h",
+ "arch/arm64/sysroot/include/bits/x86_64/signal.h",
+ "arch/arm64/sysroot/include/bits/x86_64/stat.h",
+ "arch/arm64/sysroot/include/byteswap.h",
+ "arch/arm64/sysroot/include/complex.h",
+ "arch/arm64/sysroot/include/cpio.h",
+ "arch/arm64/sysroot/include/crypt.h",
+ "arch/arm64/sysroot/include/ctype.h",
+ "arch/arm64/sysroot/include/dirent.h",
+ "arch/arm64/sysroot/include/dlfcn.h",
+ "arch/arm64/sysroot/include/elf.h",
+ "arch/arm64/sysroot/include/endian.h",
+ "arch/arm64/sysroot/include/err.h",
+ "arch/arm64/sysroot/include/errno.h",
+ "arch/arm64/sysroot/include/fcntl.h",
+ "arch/arm64/sysroot/include/features.h",
+ "arch/arm64/sysroot/include/fenv.h",
+ "arch/arm64/sysroot/include/float.h",
+ "arch/arm64/sysroot/include/fmtmsg.h",
+ "arch/arm64/sysroot/include/fnmatch.h",
+ "arch/arm64/sysroot/include/ftw.h",
+ "arch/arm64/sysroot/include/getopt.h",
+ "arch/arm64/sysroot/include/glob.h",
+ "arch/arm64/sysroot/include/grp.h",
+ "arch/arm64/sysroot/include/iconv.h",
+ "arch/arm64/sysroot/include/ifaddrs.h",
+ "arch/arm64/sysroot/include/inttypes.h",
+ "arch/arm64/sysroot/include/iso646.h",
+ "arch/arm64/sysroot/include/langinfo.h",
+ "arch/arm64/sysroot/include/libgen.h",
+ "arch/arm64/sysroot/include/limits.h",
+ "arch/arm64/sysroot/include/link.h",
+ "arch/arm64/sysroot/include/locale.h",
+ "arch/arm64/sysroot/include/malloc.h",
+ "arch/arm64/sysroot/include/math.h",
+ "arch/arm64/sysroot/include/memory.h",
+ "arch/arm64/sysroot/include/monetary.h",
+ "arch/arm64/sysroot/include/net/ethernet.h",
+ "arch/arm64/sysroot/include/net/if.h",
+ "arch/arm64/sysroot/include/net/if_arp.h",
+ "arch/arm64/sysroot/include/net/route.h",
+ "arch/arm64/sysroot/include/netdb.h",
+ "arch/arm64/sysroot/include/netinet/ether.h",
+ "arch/arm64/sysroot/include/netinet/icmp6.h",
+ "arch/arm64/sysroot/include/netinet/if_ether.h",
+ "arch/arm64/sysroot/include/netinet/igmp.h",
+ "arch/arm64/sysroot/include/netinet/in.h",
+ "arch/arm64/sysroot/include/netinet/in_systm.h",
+ "arch/arm64/sysroot/include/netinet/ip.h",
+ "arch/arm64/sysroot/include/netinet/ip6.h",
+ "arch/arm64/sysroot/include/netinet/ip_icmp.h",
+ "arch/arm64/sysroot/include/netinet/tcp.h",
+ "arch/arm64/sysroot/include/netinet/udp.h",
+ "arch/arm64/sysroot/include/netpacket/packet.h",
+ "arch/arm64/sysroot/include/nl_types.h",
+ "arch/arm64/sysroot/include/paths.h",
+ "arch/arm64/sysroot/include/poll.h",
+ "arch/arm64/sysroot/include/pthread.h",
+ "arch/arm64/sysroot/include/pty.h",
+ "arch/arm64/sysroot/include/pwd.h",
+ "arch/arm64/sysroot/include/regex.h",
+ "arch/arm64/sysroot/include/resolv.h",
+ "arch/arm64/sysroot/include/sched.h",
+ "arch/arm64/sysroot/include/search.h",
+ "arch/arm64/sysroot/include/semaphore.h",
+ "arch/arm64/sysroot/include/setjmp.h",
+ "arch/arm64/sysroot/include/signal.h",
+ "arch/arm64/sysroot/include/spawn.h",
+ "arch/arm64/sysroot/include/stdio.h",
+ "arch/arm64/sysroot/include/stdlib.h",
+ "arch/arm64/sysroot/include/stdnoreturn.h",
+ "arch/arm64/sysroot/include/string.h",
+ "arch/arm64/sysroot/include/strings.h",
+ "arch/arm64/sysroot/include/stropts.h",
+ "arch/arm64/sysroot/include/sys/acct.h",
+ "arch/arm64/sysroot/include/sys/auxv.h",
+ "arch/arm64/sysroot/include/sys/dir.h",
+ "arch/arm64/sysroot/include/sys/errno.h",
+ "arch/arm64/sysroot/include/sys/eventfd.h",
+ "arch/arm64/sysroot/include/sys/fcntl.h",
+ "arch/arm64/sysroot/include/sys/file.h",
+ "arch/arm64/sysroot/include/sys/fsuid.h",
+ "arch/arm64/sysroot/include/sys/io.h",
+ "arch/arm64/sysroot/include/sys/ioctl.h",
+ "arch/arm64/sysroot/include/sys/ipc.h",
+ "arch/arm64/sysroot/include/sys/klog.h",
+ "arch/arm64/sysroot/include/sys/mman.h",
+ "arch/arm64/sysroot/include/sys/mount.h",
+ "arch/arm64/sysroot/include/sys/msg.h",
+ "arch/arm64/sysroot/include/sys/mtio.h",
+ "arch/arm64/sysroot/include/sys/param.h",
+ "arch/arm64/sysroot/include/sys/personality.h",
+ "arch/arm64/sysroot/include/sys/poll.h",
+ "arch/arm64/sysroot/include/sys/quota.h",
+ "arch/arm64/sysroot/include/sys/random.h",
+ "arch/arm64/sysroot/include/sys/reboot.h",
+ "arch/arm64/sysroot/include/sys/reg.h",
+ "arch/arm64/sysroot/include/sys/select.h",
+ "arch/arm64/sysroot/include/sys/sem.h",
+ "arch/arm64/sysroot/include/sys/sendfile.h",
+ "arch/arm64/sysroot/include/sys/shm.h",
+ "arch/arm64/sysroot/include/sys/signal.h",
+ "arch/arm64/sysroot/include/sys/signalfd.h",
+ "arch/arm64/sysroot/include/sys/socket.h",
+ "arch/arm64/sysroot/include/sys/stat.h",
+ "arch/arm64/sysroot/include/sys/statfs.h",
+ "arch/arm64/sysroot/include/sys/statvfs.h",
+ "arch/arm64/sysroot/include/sys/stropts.h",
+ "arch/arm64/sysroot/include/sys/swap.h",
+ "arch/arm64/sysroot/include/sys/syslog.h",
+ "arch/arm64/sysroot/include/sys/termios.h",
+ "arch/arm64/sysroot/include/sys/time.h",
+ "arch/arm64/sysroot/include/sys/timeb.h",
+ "arch/arm64/sysroot/include/sys/timerfd.h",
+ "arch/arm64/sysroot/include/sys/times.h",
+ "arch/arm64/sysroot/include/sys/timex.h",
+ "arch/arm64/sysroot/include/sys/ttydefaults.h",
+ "arch/arm64/sysroot/include/sys/types.h",
+ "arch/arm64/sysroot/include/sys/ucontext.h",
+ "arch/arm64/sysroot/include/sys/uio.h",
+ "arch/arm64/sysroot/include/sys/un.h",
+ "arch/arm64/sysroot/include/sys/utsname.h",
+ "arch/arm64/sysroot/include/sys/vfs.h",
+ "arch/arm64/sysroot/include/sys/wait.h",
+ "arch/arm64/sysroot/include/sysexits.h",
+ "arch/arm64/sysroot/include/syslog.h",
+ "arch/arm64/sysroot/include/tar.h",
+ "arch/arm64/sysroot/include/termios.h",
+ "arch/arm64/sysroot/include/threads.h",
+ "arch/arm64/sysroot/include/time.h",
+ "arch/arm64/sysroot/include/uchar.h",
+ "arch/arm64/sysroot/include/ucontext.h",
+ "arch/arm64/sysroot/include/unistd.h",
+ "arch/arm64/sysroot/include/utime.h",
+ "arch/arm64/sysroot/include/values.h",
+ "arch/arm64/sysroot/include/wait.h",
+ "arch/arm64/sysroot/include/wchar.h",
+ "arch/arm64/sysroot/include/wctype.h",
+ "arch/arm64/sysroot/include/wordexp.h",
+ "arch/arm64/sysroot/include/zircon/assert.h",
+ "arch/arm64/sysroot/include/zircon/boot/bootdata.h",
+ "arch/arm64/sysroot/include/zircon/boot/driver-config.h",
+ "arch/arm64/sysroot/include/zircon/boot/e820.h",
+ "arch/arm64/sysroot/include/zircon/boot/image.h",
+ "arch/arm64/sysroot/include/zircon/boot/multiboot.h",
+ "arch/arm64/sysroot/include/zircon/boot/netboot.h",
+ "arch/arm64/sysroot/include/zircon/boot/sysconfig.h",
+ "arch/arm64/sysroot/include/zircon/compiler.h",
+ "arch/arm64/sysroot/include/zircon/device/ioctl-wrapper.h",
+ "arch/arm64/sysroot/include/zircon/device/ioctl.h",
+ "arch/arm64/sysroot/include/zircon/device/ramdisk.h",
+ "arch/arm64/sysroot/include/zircon/dlfcn.h",
+ "arch/arm64/sysroot/include/zircon/driver/binding.h",
+ "arch/arm64/sysroot/include/zircon/errors.h",
+ "arch/arm64/sysroot/include/zircon/features.h",
+ "arch/arm64/sysroot/include/zircon/fidl.h",
+ "arch/arm64/sysroot/include/zircon/hw/gpt.h",
+ "arch/arm64/sysroot/include/zircon/hw/i2c.h",
+ "arch/arm64/sysroot/include/zircon/hw/pci.h",
+ "arch/arm64/sysroot/include/zircon/hw/usb-audio.h",
+ "arch/arm64/sysroot/include/zircon/hw/usb-cdc.h",
+ "arch/arm64/sysroot/include/zircon/hw/usb-hid.h",
+ "arch/arm64/sysroot/include/zircon/hw/usb-hub.h",
+ "arch/arm64/sysroot/include/zircon/hw/usb-mass-storage.h",
+ "arch/arm64/sysroot/include/zircon/hw/usb-video.h",
+ "arch/arm64/sysroot/include/zircon/hw/usb.h",
+ "arch/arm64/sysroot/include/zircon/limits.h",
+ "arch/arm64/sysroot/include/zircon/listnode.h",
+ "arch/arm64/sysroot/include/zircon/pixelformat.h",
+ "arch/arm64/sysroot/include/zircon/process.h",
+ "arch/arm64/sysroot/include/zircon/processargs.h",
+ "arch/arm64/sysroot/include/zircon/rights.h",
+ "arch/arm64/sysroot/include/zircon/sanitizer.h",
+ "arch/arm64/sysroot/include/zircon/status.h",
+ "arch/arm64/sysroot/include/zircon/syscalls.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/debug.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/definitions.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/exception.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/hypervisor.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/iommu.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/log.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/object.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/pci.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/policy.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/port.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/profile.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/resource.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/smc.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/system.h",
+ "arch/arm64/sysroot/include/zircon/syscalls/types.h",
+ "arch/arm64/sysroot/include/zircon/threads.h",
+ "arch/arm64/sysroot/include/zircon/time.h",
+ "arch/arm64/sysroot/include/zircon/tls.h",
+ "arch/arm64/sysroot/include/zircon/types.h"
+ ],
+ "include_dir": "arch/arm64/sysroot/include",
+ "link_libs": [
+ "arch/arm64/sysroot/lib/Scrt1.o",
+ "arch/arm64/sysroot/lib/libc.so",
+ "arch/arm64/sysroot/lib/libdl.so",
+ "arch/arm64/sysroot/lib/libm.so",
+ "arch/arm64/sysroot/lib/libpthread.so",
+ "arch/arm64/sysroot/lib/librt.so",
+ "arch/arm64/sysroot/lib/libzircon.so"
+ ],
+ "root": "arch/arm64/sysroot"
+ },
+ "x64": {
+ "debug_libs": [
+ "arch/x64/sysroot/debug/libc.so",
+ "arch/x64/sysroot/debug/libzircon.so"
+ ],
+ "dist_libs": [
+ "arch/x64/sysroot/dist/lib/ld.so.1"
+ ],
+ "headers": [
+ "arch/x64/sysroot/include/alloca.h",
+ "arch/x64/sysroot/include/ar.h",
+ "arch/x64/sysroot/include/arpa/ftp.h",
+ "arch/x64/sysroot/include/arpa/inet.h",
+ "arch/x64/sysroot/include/arpa/nameser.h",
+ "arch/x64/sysroot/include/arpa/nameser_compat.h",
+ "arch/x64/sysroot/include/arpa/telnet.h",
+ "arch/x64/sysroot/include/arpa/tftp.h",
+ "arch/x64/sysroot/include/assert.h",
+ "arch/x64/sysroot/include/bits/aarch64/endian.h",
+ "arch/x64/sysroot/include/bits/aarch64/fenv.h",
+ "arch/x64/sysroot/include/bits/aarch64/float.h",
+ "arch/x64/sysroot/include/bits/aarch64/io.h",
+ "arch/x64/sysroot/include/bits/aarch64/ioctl.h",
+ "arch/x64/sysroot/include/bits/aarch64/ipc.h",
+ "arch/x64/sysroot/include/bits/aarch64/reg.h",
+ "arch/x64/sysroot/include/bits/aarch64/setjmp.h",
+ "arch/x64/sysroot/include/bits/aarch64/signal.h",
+ "arch/x64/sysroot/include/bits/aarch64/stat.h",
+ "arch/x64/sysroot/include/bits/alltypes.h",
+ "arch/x64/sysroot/include/bits/endian.h",
+ "arch/x64/sysroot/include/bits/errno.h",
+ "arch/x64/sysroot/include/bits/fcntl.h",
+ "arch/x64/sysroot/include/bits/fenv.h",
+ "arch/x64/sysroot/include/bits/float.h",
+ "arch/x64/sysroot/include/bits/io.h",
+ "arch/x64/sysroot/include/bits/ioctl.h",
+ "arch/x64/sysroot/include/bits/ipc.h",
+ "arch/x64/sysroot/include/bits/limits.h",
+ "arch/x64/sysroot/include/bits/msg.h",
+ "arch/x64/sysroot/include/bits/null.h",
+ "arch/x64/sysroot/include/bits/poll.h",
+ "arch/x64/sysroot/include/bits/posix.h",
+ "arch/x64/sysroot/include/bits/reg.h",
+ "arch/x64/sysroot/include/bits/resource.h",
+ "arch/x64/sysroot/include/bits/sem.h",
+ "arch/x64/sysroot/include/bits/setjmp.h",
+ "arch/x64/sysroot/include/bits/shm.h",
+ "arch/x64/sysroot/include/bits/signal.h",
+ "arch/x64/sysroot/include/bits/socket.h",
+ "arch/x64/sysroot/include/bits/stat.h",
+ "arch/x64/sysroot/include/bits/statfs.h",
+ "arch/x64/sysroot/include/bits/termios.h",
+ "arch/x64/sysroot/include/bits/x86_64/endian.h",
+ "arch/x64/sysroot/include/bits/x86_64/fenv.h",
+ "arch/x64/sysroot/include/bits/x86_64/float.h",
+ "arch/x64/sysroot/include/bits/x86_64/io.h",
+ "arch/x64/sysroot/include/bits/x86_64/ioctl.h",
+ "arch/x64/sysroot/include/bits/x86_64/ipc.h",
+ "arch/x64/sysroot/include/bits/x86_64/reg.h",
+ "arch/x64/sysroot/include/bits/x86_64/setjmp.h",
+ "arch/x64/sysroot/include/bits/x86_64/signal.h",
+ "arch/x64/sysroot/include/bits/x86_64/stat.h",
+ "arch/x64/sysroot/include/byteswap.h",
+ "arch/x64/sysroot/include/complex.h",
+ "arch/x64/sysroot/include/cpio.h",
+ "arch/x64/sysroot/include/crypt.h",
+ "arch/x64/sysroot/include/ctype.h",
+ "arch/x64/sysroot/include/dirent.h",
+ "arch/x64/sysroot/include/dlfcn.h",
+ "arch/x64/sysroot/include/elf.h",
+ "arch/x64/sysroot/include/endian.h",
+ "arch/x64/sysroot/include/err.h",
+ "arch/x64/sysroot/include/errno.h",
+ "arch/x64/sysroot/include/fcntl.h",
+ "arch/x64/sysroot/include/features.h",
+ "arch/x64/sysroot/include/fenv.h",
+ "arch/x64/sysroot/include/float.h",
+ "arch/x64/sysroot/include/fmtmsg.h",
+ "arch/x64/sysroot/include/fnmatch.h",
+ "arch/x64/sysroot/include/ftw.h",
+ "arch/x64/sysroot/include/getopt.h",
+ "arch/x64/sysroot/include/glob.h",
+ "arch/x64/sysroot/include/grp.h",
+ "arch/x64/sysroot/include/iconv.h",
+ "arch/x64/sysroot/include/ifaddrs.h",
+ "arch/x64/sysroot/include/inttypes.h",
+ "arch/x64/sysroot/include/iso646.h",
+ "arch/x64/sysroot/include/langinfo.h",
+ "arch/x64/sysroot/include/libgen.h",
+ "arch/x64/sysroot/include/limits.h",
+ "arch/x64/sysroot/include/link.h",
+ "arch/x64/sysroot/include/locale.h",
+ "arch/x64/sysroot/include/malloc.h",
+ "arch/x64/sysroot/include/math.h",
+ "arch/x64/sysroot/include/memory.h",
+ "arch/x64/sysroot/include/monetary.h",
+ "arch/x64/sysroot/include/net/ethernet.h",
+ "arch/x64/sysroot/include/net/if.h",
+ "arch/x64/sysroot/include/net/if_arp.h",
+ "arch/x64/sysroot/include/net/route.h",
+ "arch/x64/sysroot/include/netdb.h",
+ "arch/x64/sysroot/include/netinet/ether.h",
+ "arch/x64/sysroot/include/netinet/icmp6.h",
+ "arch/x64/sysroot/include/netinet/if_ether.h",
+ "arch/x64/sysroot/include/netinet/igmp.h",
+ "arch/x64/sysroot/include/netinet/in.h",
+ "arch/x64/sysroot/include/netinet/in_systm.h",
+ "arch/x64/sysroot/include/netinet/ip.h",
+ "arch/x64/sysroot/include/netinet/ip6.h",
+ "arch/x64/sysroot/include/netinet/ip_icmp.h",
+ "arch/x64/sysroot/include/netinet/tcp.h",
+ "arch/x64/sysroot/include/netinet/udp.h",
+ "arch/x64/sysroot/include/netpacket/packet.h",
+ "arch/x64/sysroot/include/nl_types.h",
+ "arch/x64/sysroot/include/paths.h",
+ "arch/x64/sysroot/include/poll.h",
+ "arch/x64/sysroot/include/pthread.h",
+ "arch/x64/sysroot/include/pty.h",
+ "arch/x64/sysroot/include/pwd.h",
+ "arch/x64/sysroot/include/regex.h",
+ "arch/x64/sysroot/include/resolv.h",
+ "arch/x64/sysroot/include/sched.h",
+ "arch/x64/sysroot/include/search.h",
+ "arch/x64/sysroot/include/semaphore.h",
+ "arch/x64/sysroot/include/setjmp.h",
+ "arch/x64/sysroot/include/signal.h",
+ "arch/x64/sysroot/include/spawn.h",
+ "arch/x64/sysroot/include/stdio.h",
+ "arch/x64/sysroot/include/stdlib.h",
+ "arch/x64/sysroot/include/stdnoreturn.h",
+ "arch/x64/sysroot/include/string.h",
+ "arch/x64/sysroot/include/strings.h",
+ "arch/x64/sysroot/include/stropts.h",
+ "arch/x64/sysroot/include/sys/acct.h",
+ "arch/x64/sysroot/include/sys/auxv.h",
+ "arch/x64/sysroot/include/sys/dir.h",
+ "arch/x64/sysroot/include/sys/errno.h",
+ "arch/x64/sysroot/include/sys/eventfd.h",
+ "arch/x64/sysroot/include/sys/fcntl.h",
+ "arch/x64/sysroot/include/sys/file.h",
+ "arch/x64/sysroot/include/sys/fsuid.h",
+ "arch/x64/sysroot/include/sys/io.h",
+ "arch/x64/sysroot/include/sys/ioctl.h",
+ "arch/x64/sysroot/include/sys/ipc.h",
+ "arch/x64/sysroot/include/sys/klog.h",
+ "arch/x64/sysroot/include/sys/mman.h",
+ "arch/x64/sysroot/include/sys/mount.h",
+ "arch/x64/sysroot/include/sys/msg.h",
+ "arch/x64/sysroot/include/sys/mtio.h",
+ "arch/x64/sysroot/include/sys/param.h",
+ "arch/x64/sysroot/include/sys/personality.h",
+ "arch/x64/sysroot/include/sys/poll.h",
+ "arch/x64/sysroot/include/sys/quota.h",
+ "arch/x64/sysroot/include/sys/random.h",
+ "arch/x64/sysroot/include/sys/reboot.h",
+ "arch/x64/sysroot/include/sys/reg.h",
+ "arch/x64/sysroot/include/sys/select.h",
+ "arch/x64/sysroot/include/sys/sem.h",
+ "arch/x64/sysroot/include/sys/sendfile.h",
+ "arch/x64/sysroot/include/sys/shm.h",
+ "arch/x64/sysroot/include/sys/signal.h",
+ "arch/x64/sysroot/include/sys/signalfd.h",
+ "arch/x64/sysroot/include/sys/socket.h",
+ "arch/x64/sysroot/include/sys/stat.h",
+ "arch/x64/sysroot/include/sys/statfs.h",
+ "arch/x64/sysroot/include/sys/statvfs.h",
+ "arch/x64/sysroot/include/sys/stropts.h",
+ "arch/x64/sysroot/include/sys/swap.h",
+ "arch/x64/sysroot/include/sys/syslog.h",
+ "arch/x64/sysroot/include/sys/termios.h",
+ "arch/x64/sysroot/include/sys/time.h",
+ "arch/x64/sysroot/include/sys/timeb.h",
+ "arch/x64/sysroot/include/sys/timerfd.h",
+ "arch/x64/sysroot/include/sys/times.h",
+ "arch/x64/sysroot/include/sys/timex.h",
+ "arch/x64/sysroot/include/sys/ttydefaults.h",
+ "arch/x64/sysroot/include/sys/types.h",
+ "arch/x64/sysroot/include/sys/ucontext.h",
+ "arch/x64/sysroot/include/sys/uio.h",
+ "arch/x64/sysroot/include/sys/un.h",
+ "arch/x64/sysroot/include/sys/utsname.h",
+ "arch/x64/sysroot/include/sys/vfs.h",
+ "arch/x64/sysroot/include/sys/wait.h",
+ "arch/x64/sysroot/include/sysexits.h",
+ "arch/x64/sysroot/include/syslog.h",
+ "arch/x64/sysroot/include/tar.h",
+ "arch/x64/sysroot/include/termios.h",
+ "arch/x64/sysroot/include/threads.h",
+ "arch/x64/sysroot/include/time.h",
+ "arch/x64/sysroot/include/uchar.h",
+ "arch/x64/sysroot/include/ucontext.h",
+ "arch/x64/sysroot/include/unistd.h",
+ "arch/x64/sysroot/include/utime.h",
+ "arch/x64/sysroot/include/values.h",
+ "arch/x64/sysroot/include/wait.h",
+ "arch/x64/sysroot/include/wchar.h",
+ "arch/x64/sysroot/include/wctype.h",
+ "arch/x64/sysroot/include/wordexp.h",
+ "arch/x64/sysroot/include/zircon/assert.h",
+ "arch/x64/sysroot/include/zircon/boot/bootdata.h",
+ "arch/x64/sysroot/include/zircon/boot/driver-config.h",
+ "arch/x64/sysroot/include/zircon/boot/e820.h",
+ "arch/x64/sysroot/include/zircon/boot/image.h",
+ "arch/x64/sysroot/include/zircon/boot/multiboot.h",
+ "arch/x64/sysroot/include/zircon/boot/netboot.h",
+ "arch/x64/sysroot/include/zircon/boot/sysconfig.h",
+ "arch/x64/sysroot/include/zircon/compiler.h",
+ "arch/x64/sysroot/include/zircon/device/ioctl-wrapper.h",
+ "arch/x64/sysroot/include/zircon/device/ioctl.h",
+ "arch/x64/sysroot/include/zircon/device/ramdisk.h",
+ "arch/x64/sysroot/include/zircon/dlfcn.h",
+ "arch/x64/sysroot/include/zircon/driver/binding.h",
+ "arch/x64/sysroot/include/zircon/errors.h",
+ "arch/x64/sysroot/include/zircon/features.h",
+ "arch/x64/sysroot/include/zircon/fidl.h",
+ "arch/x64/sysroot/include/zircon/hw/gpt.h",
+ "arch/x64/sysroot/include/zircon/hw/i2c.h",
+ "arch/x64/sysroot/include/zircon/hw/pci.h",
+ "arch/x64/sysroot/include/zircon/hw/usb-audio.h",
+ "arch/x64/sysroot/include/zircon/hw/usb-cdc.h",
+ "arch/x64/sysroot/include/zircon/hw/usb-hid.h",
+ "arch/x64/sysroot/include/zircon/hw/usb-hub.h",
+ "arch/x64/sysroot/include/zircon/hw/usb-mass-storage.h",
+ "arch/x64/sysroot/include/zircon/hw/usb-video.h",
+ "arch/x64/sysroot/include/zircon/hw/usb.h",
+ "arch/x64/sysroot/include/zircon/limits.h",
+ "arch/x64/sysroot/include/zircon/listnode.h",
+ "arch/x64/sysroot/include/zircon/pixelformat.h",
+ "arch/x64/sysroot/include/zircon/process.h",
+ "arch/x64/sysroot/include/zircon/processargs.h",
+ "arch/x64/sysroot/include/zircon/rights.h",
+ "arch/x64/sysroot/include/zircon/sanitizer.h",
+ "arch/x64/sysroot/include/zircon/status.h",
+ "arch/x64/sysroot/include/zircon/syscalls.h",
+ "arch/x64/sysroot/include/zircon/syscalls/debug.h",
+ "arch/x64/sysroot/include/zircon/syscalls/definitions.h",
+ "arch/x64/sysroot/include/zircon/syscalls/exception.h",
+ "arch/x64/sysroot/include/zircon/syscalls/hypervisor.h",
+ "arch/x64/sysroot/include/zircon/syscalls/iommu.h",
+ "arch/x64/sysroot/include/zircon/syscalls/log.h",
+ "arch/x64/sysroot/include/zircon/syscalls/object.h",
+ "arch/x64/sysroot/include/zircon/syscalls/pci.h",
+ "arch/x64/sysroot/include/zircon/syscalls/policy.h",
+ "arch/x64/sysroot/include/zircon/syscalls/port.h",
+ "arch/x64/sysroot/include/zircon/syscalls/profile.h",
+ "arch/x64/sysroot/include/zircon/syscalls/resource.h",
+ "arch/x64/sysroot/include/zircon/syscalls/smc.h",
+ "arch/x64/sysroot/include/zircon/syscalls/system.h",
+ "arch/x64/sysroot/include/zircon/syscalls/types.h",
+ "arch/x64/sysroot/include/zircon/threads.h",
+ "arch/x64/sysroot/include/zircon/time.h",
+ "arch/x64/sysroot/include/zircon/tls.h",
+ "arch/x64/sysroot/include/zircon/types.h"
+ ],
+ "include_dir": "arch/x64/sysroot/include",
+ "link_libs": [
+ "arch/x64/sysroot/lib/Scrt1.o",
+ "arch/x64/sysroot/lib/libc.so",
+ "arch/x64/sysroot/lib/libdl.so",
+ "arch/x64/sysroot/lib/libm.so",
+ "arch/x64/sysroot/lib/libpthread.so",
+ "arch/x64/sysroot/lib/librt.so",
+ "arch/x64/sysroot/lib/libzircon.so"
+ ],
+ "root": "arch/x64/sysroot"
+ }
+ }
+}
\ No newline at end of file
diff --git a/pkg/trace-engine/meta.json b/pkg/trace-engine/meta.json
new file mode 100644
index 0000000..8856e4e
--- /dev/null
+++ b/pkg/trace-engine/meta.json
@@ -0,0 +1,21 @@
+{
+ "binaries": {
+ "arm64": {
+ "debug": ".build-id/e7/2a14a0b645f41576a9d25e89239df92cd488d0.debug",
+ "dist": "arch/arm64/dist/libtrace-engine.so",
+ "link": "arch/arm64/lib/libtrace-engine.so"
+ },
+ "x64": {
+ "debug": ".build-id/db/dab49d734285639cadc0b2eb151b302ecb2edf.debug",
+ "dist": "arch/x64/dist/libtrace-engine.so",
+ "link": "arch/x64/lib/libtrace-engine.so"
+ }
+ },
+ "deps": [],
+ "format": "shared",
+ "headers": [],
+ "include_dir": "pkg/trace-engine/include",
+ "name": "trace-engine",
+ "root": "pkg/trace-engine",
+ "type": "cc_prebuilt_library"
+}
\ No newline at end of file
diff --git a/pkg/vulkan/meta.json b/pkg/vulkan/meta.json
new file mode 100644
index 0000000..440ecf0
--- /dev/null
+++ b/pkg/vulkan/meta.json
@@ -0,0 +1,24 @@
+{
+ "binaries": {
+ "arm64": {
+ "debug": ".build-id/13/1673e529cae5f1.debug",
+ "dist": "arch/arm64/dist/libvulkan.so",
+ "link": "arch/arm64/lib/libvulkan.so"
+ },
+ "x64": {
+ "debug": ".build-id/72/f8518d550ec0aa.debug",
+ "dist": "arch/x64/dist/libvulkan.so",
+ "link": "arch/x64/lib/libvulkan.so"
+ }
+ },
+ "deps": [
+ "fdio",
+ "trace-engine"
+ ],
+ "format": "shared",
+ "headers": [],
+ "include_dir": "pkg/vulkan/include",
+ "name": "vulkan",
+ "root": "pkg/vulkan",
+ "type": "cc_prebuilt_library"
+}
\ No newline at end of file
diff --git a/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_core_validation.json b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_core_validation.json
new file mode 100644
index 0000000..94887e6
--- /dev/null
+++ b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_core_validation.json
@@ -0,0 +1,43 @@
+{
+ "file_format_version" : "1.1.0",
+ "layer" : {
+ "name": "VK_LAYER_LUNARG_core_validation",
+ "type": "GLOBAL",
+ "library_path": "libVkLayer_core_validation.so",
+ "api_version": "1.0.68",
+ "implementation_version": "1",
+ "description": "LunarG Validation Layer",
+ "instance_extensions": [
+ {
+ "name": "VK_EXT_debug_report",
+ "spec_version": "6"
+ }
+ ],
+ "device_extensions": [
+ {
+ "name": "VK_EXT_debug_marker",
+ "spec_version": "4",
+ "entrypoints": ["vkDebugMarkerSetObjectTagEXT",
+ "vkDebugMarkerSetObjectNameEXT",
+ "vkCmdDebugMarkerBeginEXT",
+ "vkCmdDebugMarkerEndEXT",
+ "vkCmdDebugMarkerInsertEXT"
+ ]
+ },
+ {
+ "name": "VK_EXT_validation_cache",
+ "spec_version": "1",
+ "entrypoints": ["vkCreateValidationCacheEXT",
+ "vkDestroyValidationCacheEXT",
+ "vkGetValidationCacheDataEXT",
+ "vkMergeValidationCachesEXT"
+ ]
+ }
+ ]
+ }
+}
+
+
+
+
+
diff --git a/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json
new file mode 100644
index 0000000..7c6ebe6
--- /dev/null
+++ b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json
@@ -0,0 +1,21 @@
+{
+ "file_format_version" : "1.0.0",
+ "layer" : {
+ "name": "VK_LAYER_GOOGLE_image_pipe_swapchain",
+ "type": "GLOBAL",
+ "library_path": "libVkLayer_image_pipe_swapchain.so",
+ "api_version": "1.0.38",
+ "implementation_version": "1",
+ "description": "Image Pipe Swapchain",
+ "disable_environment": {
+ "DISABLE_IMAGEPIPE_SWAPCHAIN_LAYER": "1"
+ },
+ "instance_extensions": [
+ { "name": "VK_KHR_surface", "spec_version": "25" },
+ { "name": "VK_KHR_magma_surface", "spec_version": "1" }
+ ],
+ "device_extensions": [
+ { "name": "VK_KHR_swapchain", "spec_version": "68" }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_object_tracker.json b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_object_tracker.json
new file mode 100644
index 0000000..3da8c58
--- /dev/null
+++ b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_object_tracker.json
@@ -0,0 +1,29 @@
+{
+ "file_format_version" : "1.1.0",
+ "layer" : {
+ "name": "VK_LAYER_LUNARG_object_tracker",
+ "type": "GLOBAL",
+ "library_path": "libVkLayer_object_tracker.so",
+ "api_version": "1.0.68",
+ "implementation_version": "1",
+ "description": "LunarG Validation Layer",
+ "instance_extensions": [
+ {
+ "name": "VK_EXT_debug_report",
+ "spec_version": "6"
+ }
+ ],
+ "device_extensions": [
+ {
+ "name": "VK_EXT_debug_marker",
+ "spec_version": "4",
+ "entrypoints": ["vkDebugMarkerSetObjectTagEXT",
+ "vkDebugMarkerSetObjectNameEXT",
+ "vkCmdDebugMarkerBeginEXT",
+ "vkCmdDebugMarkerEndEXT",
+ "vkCmdDebugMarkerInsertEXT"
+ ]
+ }
+ ]
+ }
+}
diff --git a/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_parameter_validation.json b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_parameter_validation.json
new file mode 100644
index 0000000..83018ed
--- /dev/null
+++ b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_parameter_validation.json
@@ -0,0 +1,29 @@
+{
+ "file_format_version" : "1.1.0",
+ "layer" : {
+ "name": "VK_LAYER_LUNARG_parameter_validation",
+ "type": "GLOBAL",
+ "library_path": "libVkLayer_parameter_validation.so",
+ "api_version": "1.0.68",
+ "implementation_version": "1",
+ "description": "LunarG Validation Layer",
+ "instance_extensions": [
+ {
+ "name": "VK_EXT_debug_report",
+ "spec_version": "6"
+ }
+ ],
+ "device_extensions": [
+ {
+ "name": "VK_EXT_debug_marker",
+ "spec_version": "4",
+ "entrypoints": ["vkDebugMarkerSetObjectTagEXT",
+ "vkDebugMarkerSetObjectNameEXT",
+ "vkCmdDebugMarkerBeginEXT",
+ "vkCmdDebugMarkerEndEXT",
+ "vkCmdDebugMarkerInsertEXT"
+ ]
+ }
+ ]
+ }
+}
diff --git a/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_standard_validation.json b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_standard_validation.json
new file mode 100644
index 0000000..ccf4189
--- /dev/null
+++ b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_standard_validation.json
@@ -0,0 +1,17 @@
+{
+ "file_format_version": "1.1.1",
+ "layer": {
+ "name": "VK_LAYER_LUNARG_standard_validation",
+ "type": "GLOBAL",
+ "api_version": "1.0.68",
+ "implementation_version": "1",
+ "description": "LunarG Standard Validation",
+ "component_layers": [
+ "VK_LAYER_GOOGLE_threading",
+ "VK_LAYER_LUNARG_parameter_validation",
+ "VK_LAYER_LUNARG_object_tracker",
+ "VK_LAYER_LUNARG_core_validation",
+ "VK_LAYER_GOOGLE_unique_objects"
+ ]
+ }
+}
diff --git a/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_threading.json b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_threading.json
new file mode 100644
index 0000000..2e162b2
--- /dev/null
+++ b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_threading.json
@@ -0,0 +1,17 @@
+{
+ "file_format_version" : "1.1.0",
+ "layer" : {
+ "name": "VK_LAYER_GOOGLE_threading",
+ "type": "GLOBAL",
+ "library_path": "libVkLayer_threading.so",
+ "api_version": "1.0.68",
+ "implementation_version": "1",
+ "description": "Google Validation Layer",
+ "instance_extensions": [
+ {
+ "name": "VK_EXT_debug_report",
+ "spec_version": "6"
+ }
+ ]
+ }
+}
diff --git a/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_unique_objects.json b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_unique_objects.json
new file mode 100644
index 0000000..82ad4e7
--- /dev/null
+++ b/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_unique_objects.json
@@ -0,0 +1,11 @@
+{
+ "file_format_version" : "1.1.0",
+ "layer" : {
+ "name": "VK_LAYER_GOOGLE_unique_objects",
+ "type": "GLOBAL",
+ "library_path": "libVkLayer_unique_objects.so",
+ "api_version": "1.0.68",
+ "implementation_version": "1",
+ "description": "Google Validation Layer"
+ }
+}
diff --git a/pkg/vulkan_layers/meta.json b/pkg/vulkan_layers/meta.json
new file mode 100644
index 0000000..f5355b7
--- /dev/null
+++ b/pkg/vulkan_layers/meta.json
@@ -0,0 +1,32 @@
+{
+ "binaries": {
+ "arm64": [
+ "arch/arm64/dist/libVkLayer_core_validation.so",
+ "arch/arm64/dist/libVkLayer_threading.so",
+ "arch/arm64/dist/libVkLayer_unique_objects.so",
+ "arch/arm64/dist/libVkLayer_parameter_validation.so",
+ "arch/arm64/dist/libVkLayer_object_tracker.so",
+ "arch/arm64/dist/libVkLayer_image_pipe_swapchain.so"
+ ],
+ "x64": [
+ "arch/x64/dist/libVkLayer_core_validation.so",
+ "arch/x64/dist/libVkLayer_threading.so",
+ "arch/x64/dist/libVkLayer_unique_objects.so",
+ "arch/x64/dist/libVkLayer_parameter_validation.so",
+ "arch/x64/dist/libVkLayer_object_tracker.so",
+ "arch/x64/dist/libVkLayer_image_pipe_swapchain.so"
+ ]
+ },
+ "name": "vulkan_layers",
+ "resources": [
+ "pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_core_validation.json",
+ "pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_parameter_validation.json",
+ "pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_threading.json",
+ "pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_unique_objects.json",
+ "pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_object_tracker.json",
+ "pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_standard_validation.json",
+ "pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json"
+ ],
+ "root": "pkg/vulkan_layers",
+ "type": "loadable_module"
+}
\ No newline at end of file
diff --git a/pkg/zx/bti.cpp b/pkg/zx/bti.cpp
new file mode 100644
index 0000000..436177d
--- /dev/null
+++ b/pkg/zx/bti.cpp
@@ -0,0 +1,15 @@
+// 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/zx/bti.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t bti::create(const iommu& iommu, uint32_t options, uint64_t bti_id, bti* result) {
+ return zx_bti_create(iommu.get(), options, bti_id, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/channel.cpp b/pkg/zx/channel.cpp
new file mode 100644
index 0000000..bbf84bb
--- /dev/null
+++ b/pkg/zx/channel.cpp
@@ -0,0 +1,25 @@
+// Copyright 2016 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/zx/channel.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t channel::create(uint32_t flags, channel* endpoint0,
+ channel* endpoint1) {
+ // Ensure aliasing of both out parameters to the same container
+ // has a well-defined result, and does not leak.
+ channel h0;
+ channel h1;
+ zx_status_t status = zx_channel_create(flags,
+ h0.reset_and_get_address(),
+ h1.reset_and_get_address());
+ endpoint0->reset(h0.release());
+ endpoint1->reset(h1.release());
+ return status;
+}
+
+} // namespace zx
diff --git a/pkg/zx/debuglog.cpp b/pkg/zx/debuglog.cpp
new file mode 100644
index 0000000..05ee86d
--- /dev/null
+++ b/pkg/zx/debuglog.cpp
@@ -0,0 +1,15 @@
+// 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/zx/debuglog.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t debuglog::create(const resource& resource, uint32_t options, debuglog* result) {
+ return zx_debuglog_create(resource.get(), options, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/event.cpp b/pkg/zx/event.cpp
new file mode 100644
index 0000000..4539a68
--- /dev/null
+++ b/pkg/zx/event.cpp
@@ -0,0 +1,15 @@
+// Copyright 2016 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/zx/event.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t event::create(uint32_t options, event* result) {
+ return zx_event_create(options, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/eventpair.cpp b/pkg/zx/eventpair.cpp
new file mode 100644
index 0000000..f561e14
--- /dev/null
+++ b/pkg/zx/eventpair.cpp
@@ -0,0 +1,25 @@
+// Copyright 2016 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/zx/eventpair.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t eventpair::create(uint32_t flags, eventpair* endpoint0,
+ eventpair* endpoint1) {
+ // Ensure aliasing of both out parameters to the same container
+ // has a well-defined result, and does not leak.
+ eventpair h0;
+ eventpair h1;
+ zx_status_t status = zx_eventpair_create(
+ flags, h0.reset_and_get_address(),
+ h1.reset_and_get_address());
+ endpoint0->reset(h0.release());
+ endpoint1->reset(h1.release());
+ return status;
+}
+
+} // namespace zx
diff --git a/pkg/zx/fifo.cpp b/pkg/zx/fifo.cpp
new file mode 100644
index 0000000..27f965f
--- /dev/null
+++ b/pkg/zx/fifo.cpp
@@ -0,0 +1,25 @@
+// Copyright 2017 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/zx/fifo.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t fifo::create(uint32_t elem_count, uint32_t elem_size,
+ uint32_t options, fifo* out0, fifo* out1) {
+ // Ensure aliasing of both out parameters to the same container
+ // has a well-defined result, and does not leak.
+ fifo h0;
+ fifo h1;
+ zx_status_t status = zx_fifo_create(
+ elem_count, elem_size, options, h0.reset_and_get_address(),
+ h1.reset_and_get_address());
+ out0->reset(h0.release());
+ out1->reset(h1.release());
+ return status;
+}
+
+} // namespace zx
diff --git a/pkg/zx/guest.cpp b/pkg/zx/guest.cpp
new file mode 100644
index 0000000..f3d6029
--- /dev/null
+++ b/pkg/zx/guest.cpp
@@ -0,0 +1,22 @@
+// 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/zx/guest.h>
+
+#include <zircon/syscalls.h>
+
+#include <lib/zx/vmar.h>
+
+namespace zx {
+
+zx_status_t guest::create(const resource& resource, uint32_t options,
+ guest* guest, vmar* vmar) {
+ // Assume |resource|, |guest| and |vmar| must refer to different containers,
+ // due to strict aliasing.
+ return zx_guest_create(
+ resource.get(), options, guest->reset_and_get_address(),
+ vmar->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/include/lib/zx/bti.h b/pkg/zx/include/lib/zx/bti.h
new file mode 100644
index 0000000..0a66716
--- /dev/null
+++ b/pkg/zx/include/lib/zx/bti.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef LIB_ZX_BTI_H_
+#define LIB_ZX_BTI_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/iommu.h>
+#include <lib/zx/object.h>
+#include <lib/zx/pmt.h>
+#include <lib/zx/vmo.h>
+
+namespace zx {
+
+class bti : public object<bti> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_BTI;
+
+ constexpr bti() = default;
+
+ explicit bti(zx_handle_t value) : object(value) {}
+
+ explicit bti(handle&& h) : object(h.release()) {}
+
+ bti(bti&& other) : object(other.release()) {}
+
+ bti& operator=(bti&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const iommu& iommu, uint32_t options, uint64_t bti_id, bti* result);
+
+ zx_status_t pin(uint32_t options, const vmo& vmo, uint64_t offset, uint64_t size,
+ zx_paddr_t* addrs, size_t addrs_count, pmt* pmt) const {
+ return zx_bti_pin(get(), options, vmo.get(), offset, size, addrs, addrs_count,
+ pmt->reset_and_get_address());
+ }
+
+ zx_status_t release_quarantine() const { return zx_bti_release_quarantine(get()); }
+};
+
+using unowned_bti = unowned<bti>;
+
+} // namespace zx
+
+#endif // LIB_ZX_BTI_H_
diff --git a/pkg/zx/include/lib/zx/channel.h b/pkg/zx/include/lib/zx/channel.h
new file mode 100644
index 0000000..bd28ca1
--- /dev/null
+++ b/pkg/zx/include/lib/zx/channel.h
@@ -0,0 +1,66 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_CHANNEL_H_
+#define LIB_ZX_CHANNEL_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <lib/zx/time.h>
+
+namespace zx {
+
+class channel : public object<channel> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_CHANNEL;
+
+ constexpr channel() = default;
+
+ explicit channel(zx_handle_t value) : object(value) {}
+
+ explicit channel(handle&& h) : object(h.release()) {}
+
+ channel(channel&& other) : object(other.release()) {}
+
+ channel& operator=(channel&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint32_t flags, channel* endpoint0,
+ channel* endpoint1);
+
+ zx_status_t read(uint32_t flags, void* bytes, uint32_t num_bytes,
+ uint32_t* actual_bytes, zx_handle_t* handles,
+ uint32_t num_handles, uint32_t* actual_handles) const {
+ return zx_channel_read(get(), flags, bytes, handles, num_bytes,
+ num_handles, actual_bytes, actual_handles);
+ }
+
+ zx_status_t read_etc(uint32_t flags, void* bytes, uint32_t num_bytes,
+ uint32_t* actual_bytes, zx_handle_info_t* handles,
+ uint32_t num_handles, uint32_t* actual_handles) const {
+ return zx_channel_read_etc(get(), flags, bytes, handles, num_bytes,
+ num_handles, actual_bytes, actual_handles);
+ }
+
+ zx_status_t write(uint32_t flags, const void* bytes, uint32_t num_bytes,
+ const zx_handle_t* handles, uint32_t num_handles) const {
+ return zx_channel_write(get(), flags, bytes, num_bytes, handles,
+ num_handles);
+ }
+
+ zx_status_t call(uint32_t flags, zx::time deadline,
+ const zx_channel_call_args_t* args,
+ uint32_t* actual_bytes, uint32_t* actual_handles) const {
+ return zx_channel_call(get(), flags, deadline.get(), args, actual_bytes,
+ actual_handles);
+ }
+};
+
+using unowned_channel = unowned<channel>;
+
+} // namespace zx
+
+#endif // LIB_ZX_CHANNEL_H_
diff --git a/pkg/zx/include/lib/zx/debuglog.h b/pkg/zx/include/lib/zx/debuglog.h
new file mode 100644
index 0000000..1b00042
--- /dev/null
+++ b/pkg/zx/include/lib/zx/debuglog.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef LIB_ZX_DEBUGLOG_H_
+#define LIB_ZX_DEBUGLOG_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <lib/zx/resource.h>
+
+namespace zx {
+
+class debuglog : public object<debuglog> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_LOG;
+
+ constexpr debuglog() = default;
+
+ explicit debuglog(zx_handle_t value) : object(value) {}
+
+ explicit debuglog(handle&& h) : object(h.release()) {}
+
+ debuglog(debuglog&& other) : object(other.release()) {}
+
+ debuglog& operator=(debuglog&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const resource& resource, uint32_t options, debuglog* result);
+
+ zx_status_t write(uint32_t options, const void* buffer, size_t buffer_size) const {
+ return zx_debuglog_write(get(), options, buffer, buffer_size);
+ }
+
+ zx_status_t read(uint32_t options, void* buffer, size_t buffer_size) const {
+ return zx_debuglog_read(get(), options, buffer, buffer_size);
+ }
+};
+
+using unowned_debuglog = unowned<debuglog>;
+
+} // namespace zx
+
+#endif // LIB_ZX_DEBUGLOG_H_
diff --git a/pkg/zx/include/lib/zx/event.h b/pkg/zx/include/lib/zx/event.h
new file mode 100644
index 0000000..590c4c1
--- /dev/null
+++ b/pkg/zx/include/lib/zx/event.h
@@ -0,0 +1,37 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_EVENT_H_
+#define LIB_ZX_EVENT_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+namespace zx {
+
+class event : public object<event> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_EVENT;
+
+ constexpr event() = default;
+
+ explicit event(zx_handle_t value) : object(value) {}
+
+ explicit event(handle&& h) : object(h.release()) {}
+
+ event(event&& other) : object(other.release()) {}
+
+ event& operator=(event&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint32_t options, event* result);
+};
+
+using unowned_event = unowned<event>;
+
+} // namespace zx
+
+#endif // LIB_ZX_EVENT_H_
diff --git a/pkg/zx/include/lib/zx/eventpair.h b/pkg/zx/include/lib/zx/eventpair.h
new file mode 100644
index 0000000..56d094a
--- /dev/null
+++ b/pkg/zx/include/lib/zx/eventpair.h
@@ -0,0 +1,38 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_EVENTPAIR_H_
+#define LIB_ZX_EVENTPAIR_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+namespace zx {
+
+class eventpair : public object<eventpair> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_EVENTPAIR;
+
+ constexpr eventpair() = default;
+
+ explicit eventpair(zx_handle_t value) : object(value) {}
+
+ explicit eventpair(handle&& h) : object(h.release()) {}
+
+ eventpair(eventpair&& other) : object(other.release()) {}
+
+ eventpair& operator=(eventpair&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint32_t options, eventpair* endpoint0,
+ eventpair* endpoint1);
+};
+
+using unowned_eventpair = unowned<eventpair>;
+
+} // namespace zx
+
+#endif // LIB_ZX_EVENTPAIR_H_
diff --git a/pkg/zx/include/lib/zx/fifo.h b/pkg/zx/include/lib/zx/fifo.h
new file mode 100644
index 0000000..aeab229
--- /dev/null
+++ b/pkg/zx/include/lib/zx/fifo.h
@@ -0,0 +1,47 @@
+// Copyright 2017 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.
+
+#ifndef LIB_ZX_FIFO_H_
+#define LIB_ZX_FIFO_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+namespace zx {
+
+class fifo : public object<fifo> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_FIFO;
+
+ constexpr fifo() = default;
+
+ explicit fifo(zx_handle_t value) : object(value) {}
+
+ explicit fifo(handle&& h) : object(h.release()) {}
+
+ fifo(fifo&& other) : object(other.release()) {}
+
+ fifo& operator=(fifo&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint32_t elem_count, uint32_t elem_size,
+ uint32_t options, fifo* out0, fifo* out1);
+
+ zx_status_t write(size_t elem_size, const void* buffer, size_t count, size_t* actual_count) const {
+ return zx_fifo_write(get(), elem_size, buffer, count, actual_count);
+ }
+
+ zx_status_t read(size_t elem_size, void* buffer, size_t count, size_t* actual_count) const {
+ return zx_fifo_read(get(), elem_size, buffer, count, actual_count);
+ }
+
+};
+
+using unowned_fifo = unowned<fifo>;
+
+} // namespace zx
+
+#endif // LIB_ZX_FIFO_H_
diff --git a/pkg/zx/include/lib/zx/guest.h b/pkg/zx/include/lib/zx/guest.h
new file mode 100644
index 0000000..037bc6d
--- /dev/null
+++ b/pkg/zx/include/lib/zx/guest.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef LIB_ZX_GUEST_H_
+#define LIB_ZX_GUEST_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <lib/zx/port.h>
+#include <lib/zx/resource.h>
+#include <lib/zx/vmo.h>
+
+namespace zx {
+
+class guest : public object<guest> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_GUEST;
+
+ constexpr guest() = default;
+
+ explicit guest(zx_handle_t value) : object(value) {}
+
+ explicit guest(handle&& h) : object(h.release()) {}
+
+ guest(guest&& other) : object(other.release()) {}
+
+ guest& operator=(guest&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const resource& resource, uint32_t options,
+ guest* guest, vmar* vmar);
+
+ zx_status_t set_trap(uint32_t kind, zx_gpaddr_t addr, size_t len,
+ const port& port, uint64_t key) {
+ return zx_guest_set_trap(get(), kind, addr, len, port.get(), key);
+ }
+};
+
+using unowned_guest = unowned<guest>;
+
+} // namespace zx
+
+#endif // LIB_ZX_GUEST_H_
diff --git a/pkg/zx/include/lib/zx/handle.h b/pkg/zx/include/lib/zx/handle.h
new file mode 100644
index 0000000..3daca00
--- /dev/null
+++ b/pkg/zx/include/lib/zx/handle.h
@@ -0,0 +1,17 @@
+// Copyright 2017 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.
+
+#ifndef LIB_ZX_HANDLE_H_
+#define LIB_ZX_HANDLE_H_
+
+#include <lib/zx/object.h>
+
+namespace zx {
+
+using handle = object<void>;
+using unowned_handle = unowned<handle>;
+
+} // namespace zx
+
+#endif // LIB_ZX_HANDLE_H_
diff --git a/pkg/zx/include/lib/zx/interrupt.h b/pkg/zx/include/lib/zx/interrupt.h
new file mode 100644
index 0000000..b3d3eb7
--- /dev/null
+++ b/pkg/zx/include/lib/zx/interrupt.h
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef LIB_ZX_INTERRUPT_H_
+#define LIB_ZX_INTERRUPT_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <lib/zx/resource.h>
+#include <lib/zx/time.h>
+
+namespace zx {
+
+class interrupt : public object<interrupt> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_INTERRUPT;
+
+ constexpr interrupt() = default;
+
+ explicit interrupt(zx_handle_t value) : object(value) {}
+
+ explicit interrupt(handle&& h) : object(h.release()) {}
+
+ interrupt(interrupt&& other) : object(other.release()) {}
+
+ interrupt& operator=(interrupt&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const resource& resource, uint32_t vector,
+ uint32_t options, interrupt* result);
+
+ zx_status_t wait(zx::time* timestamp) {
+ return zx_interrupt_wait(get(), timestamp->get_address());
+ }
+
+ zx_status_t destroy() {
+ return zx_interrupt_destroy(get());
+ }
+
+ zx_status_t trigger(uint32_t options, zx::time timestamp) {
+ return zx_interrupt_trigger(get(), options, timestamp.get());
+ }
+
+ zx_status_t bind(zx_handle_t porth, uint64_t key, uint32_t options) {
+ return zx_interrupt_bind(get(), porth, key, options);
+ }
+
+ zx_status_t ack() {
+ return zx_interrupt_ack(get());
+ }
+};
+
+using unowned_interrupt = unowned<interrupt>;
+
+} // namespace zx
+
+#endif // LIB_ZX_INTERRUPT_H_
diff --git a/pkg/zx/include/lib/zx/iommu.h b/pkg/zx/include/lib/zx/iommu.h
new file mode 100644
index 0000000..95dc427
--- /dev/null
+++ b/pkg/zx/include/lib/zx/iommu.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef LIB_ZX_IOMMU_H_
+#define LIB_ZX_IOMMU_H_
+
+#include <lib/zx/object.h>
+#include <lib/zx/resource.h>
+
+namespace zx {
+
+class iommu : public object<iommu> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_IOMMU;
+
+ constexpr iommu() = default;
+
+ explicit iommu(zx_handle_t value) : object(value) {}
+
+ explicit iommu(handle&& h) : object(h.release()) {}
+
+ iommu(iommu&& other) : object(other.release()) {}
+
+ iommu& operator=(iommu&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const resource& resource, uint32_t type, const void* desc,
+ size_t desc_size, iommu* result);
+};
+
+using unowned_iommu = unowned<iommu>;
+
+} // namespace zx
+
+#endif // LIB_ZX_IOMMU_H_
diff --git a/pkg/zx/include/lib/zx/job.h b/pkg/zx/include/lib/zx/job.h
new file mode 100644
index 0000000..3bd7125
--- /dev/null
+++ b/pkg/zx/include/lib/zx/job.h
@@ -0,0 +1,63 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_JOB_H_
+#define LIB_ZX_JOB_H_
+
+#include <lib/zx/task.h>
+#include <zircon/process.h>
+
+namespace zx {
+
+class process;
+
+class job : public task<job> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_JOB;
+
+ constexpr job() = default;
+
+ explicit job(zx_handle_t value) : task(value) {}
+
+ explicit job(handle&& h) : task(h.release()) {}
+
+ job(job&& other) : task(other.release()) {}
+
+ job& operator=(job&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const zx::job& parent, uint32_t options, job* result);
+
+ // Provide strongly-typed overloads, in addition to get_child(handle*).
+ using task<job>::get_child;
+ zx_status_t get_child(uint64_t koid, zx_rights_t rights,
+ job* result) const {
+ // Allow for |result| and |this| aliasing the same container.
+ job h;
+ zx_status_t status = zx_object_get_child(
+ value_, koid, rights, h.reset_and_get_address());
+ result->reset(h.release());
+ return status;
+ }
+ zx_status_t get_child(uint64_t koid, zx_rights_t rights,
+ process* result) const;
+
+ zx_status_t set_policy(uint32_t options, uint32_t topic, const void* policy, uint32_t count) const {
+ return zx_job_set_policy(get(), options, topic, policy, count);
+ }
+
+ // Ideally this would be called zx::job::default(), but default is a
+ // C++ keyword and cannot be used as a function name.
+ static inline unowned<job> default_job() {
+ return unowned<job>(zx_job_default());
+ }
+};
+
+using unowned_job = unowned<job>;
+
+} // namespace zx
+
+#endif // LIB_ZX_JOB_H_
diff --git a/pkg/zx/include/lib/zx/log.h b/pkg/zx/include/lib/zx/log.h
new file mode 100644
index 0000000..36b6e22
--- /dev/null
+++ b/pkg/zx/include/lib/zx/log.h
@@ -0,0 +1,45 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_LOG_H_
+#define LIB_ZX_LOG_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+namespace zx {
+
+class log : public object<log> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_LOG;
+
+ constexpr log() = default;
+
+ explicit log(zx_handle_t value) : object(value) {}
+
+ explicit log(handle&& h) : object(h.release()) {}
+
+ log(log&& other) : object(other.release()) {}
+
+ log& operator=(log&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint32_t flags, log* result);
+
+ zx_status_t write(uint32_t len, const void* buffer, uint32_t flags) const {
+ return zx_log_write(get(), len, buffer, flags);
+ }
+
+ zx_status_t read(uint32_t len, void* buffer, uint32_t flags) const {
+ return zx_log_read(get(), len, buffer, flags);
+ }
+};
+
+using unowned_log = unowned<log>;
+
+} // namespace zx
+
+#endif // LIB_ZX_LOG_H_
diff --git a/pkg/zx/include/lib/zx/object.h b/pkg/zx/include/lib/zx/object.h
new file mode 100644
index 0000000..57c023c
--- /dev/null
+++ b/pkg/zx/include/lib/zx/object.h
@@ -0,0 +1,319 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_OBJECT_H_
+#define LIB_ZX_OBJECT_H_
+
+#include <zircon/syscalls.h>
+#include <zircon/types.h>
+#include <lib/zx/object_traits.h>
+#include <lib/zx/time.h>
+
+namespace zx {
+
+class port;
+class profile;
+
+// Wraps and takes ownership of a handle to an object.
+//
+// Used for code that wants to operate generically on the zx_handle_t value
+// inside a |zx::object| and doesn't otherwise need a template parameter.
+//
+// The handle is automatically closed when the wrapper is destroyed.
+class object_base {
+public:
+ void reset(zx_handle_t value = ZX_HANDLE_INVALID) {
+ close();
+ value_ = value;
+ }
+
+ bool is_valid() const { return value_ != ZX_HANDLE_INVALID; }
+ explicit operator bool() const { return is_valid(); }
+
+ zx_handle_t get() const { return value_; }
+
+ // Reset the underlying handle, and then get the address of the
+ // underlying internal handle storage.
+ //
+ // Note: The intended purpose is to facilitate interactions with C
+ // APIs which expect to be provided a pointer to a handle used as
+ // an out parameter.
+ zx_handle_t* reset_and_get_address() {
+ reset();
+ return &value_;
+ }
+
+ __attribute__((warn_unused_result)) zx_handle_t release() {
+ zx_handle_t result = value_;
+ value_ = ZX_HANDLE_INVALID;
+ return result;
+ }
+
+protected:
+ constexpr object_base() : value_(ZX_HANDLE_INVALID) {}
+
+ explicit object_base(zx_handle_t value) : value_(value) {}
+
+ ~object_base() { close(); }
+
+ object_base(const object_base&) = delete;
+
+ void operator=(const object_base&) = delete;
+
+ void close() {
+ if (value_ != ZX_HANDLE_INVALID) {
+ zx_handle_close(value_);
+ value_ = ZX_HANDLE_INVALID;
+ }
+ }
+
+ zx_handle_t value_;
+};
+
+// Provides type-safe access to operations on a handle.
+template <typename T> class object : public object_base {
+public:
+ constexpr object() = default;
+
+ explicit object(zx_handle_t value) : object_base(value) {}
+
+ template <typename U> object(object<U>&& other) : object_base(other.release()) {
+ static_assert(is_same<T, void>::value, "Receiver must be compatible.");
+ }
+
+ template <typename U> object<T>& operator=(object<U>&& other) {
+ static_assert(is_same<T, void>::value, "Receiver must be compatible.");
+ reset(other.release());
+ return *this;
+ }
+
+ void swap(object<T>& other) {
+ zx_handle_t tmp = value_;
+ value_ = other.value_;
+ other.value_ = tmp;
+ }
+
+ zx_status_t duplicate(zx_rights_t rights, object<T>* result) const {
+ static_assert(object_traits<T>::supports_duplication,
+ "Object must support duplication.");
+ zx_handle_t h = ZX_HANDLE_INVALID;
+ zx_status_t status = zx_handle_duplicate(value_, rights, &h);
+ result->reset(h);
+ return status;
+ }
+
+ zx_status_t replace(zx_rights_t rights, object<T>* result) {
+ zx_handle_t h = ZX_HANDLE_INVALID;
+ zx_status_t status = zx_handle_replace(value_, rights, &h);
+ // We store ZX_HANDLE_INVALID to value_ before calling reset on result
+ // in case result == this.
+ value_ = ZX_HANDLE_INVALID;
+ result->reset(h);
+ return status;
+ }
+
+ zx_status_t wait_one(zx_signals_t signals, zx::time deadline,
+ zx_signals_t* pending) const {
+ static_assert(object_traits<T>::supports_wait, "Object is not waitable.");
+ return zx_object_wait_one(value_, signals, deadline.get(), pending);
+ }
+
+ zx_status_t wait_async(const object<port>& port, uint64_t key,
+ zx_signals_t signals, uint32_t options) const {
+ static_assert(object_traits<T>::supports_wait, "Object is not waitable.");
+ return zx_object_wait_async(value_, port.get(), key, signals, options);
+ }
+
+ static zx_status_t wait_many(zx_wait_item_t* wait_items, uint32_t count, zx::time deadline) {
+ static_assert(object_traits<T>::supports_wait, "Object is not waitable.");
+ return zx_object_wait_many(wait_items, count, deadline.get());
+ }
+
+ zx_status_t signal(uint32_t clear_mask, uint32_t set_mask) const {
+ static_assert(object_traits<T>::supports_user_signal,
+ "Object must support user signals.");
+ return zx_object_signal(get(), clear_mask, set_mask);
+ }
+
+ zx_status_t signal_peer(uint32_t clear_mask, uint32_t set_mask) const {
+ static_assert(object_traits<T>::supports_user_signal,
+ "Object must support user signals.");
+ static_assert(object_traits<T>::has_peer_handle,
+ "Object must have peer object.");
+ return zx_object_signal_peer(get(), clear_mask, set_mask);
+ }
+
+ zx_status_t get_info(uint32_t topic, void* buffer,
+ size_t buffer_size,
+ size_t* actual_count, size_t* avail_count) const {
+ return zx_object_get_info(get(), topic, buffer, buffer_size, actual_count, avail_count);
+ }
+
+ zx_status_t get_child(uint64_t koid, zx_rights_t rights,
+ object<void>* result) const {
+ // Allow for |result| and |this| being the same container, though that
+ // can only happen for |T=void|, due to strict aliasing.
+ object<void> h;
+ zx_status_t status = zx_object_get_child(
+ value_, koid, rights, h.reset_and_get_address());
+ result->reset(h.release());
+ return status;
+ }
+
+ zx_status_t get_property(uint32_t property, void* value,
+ size_t size) const {
+ return zx_object_get_property(get(), property, value, size);
+ }
+
+ zx_status_t set_property(uint32_t property, const void* value,
+ size_t size) const {
+ return zx_object_set_property(get(), property, value, size);
+ }
+
+ zx_status_t get_cookie(const object_base& scope, uint64_t *cookie) const {
+ return zx_object_get_cookie(get(), scope.get(), cookie);
+ }
+
+ zx_status_t set_cookie(const object_base& scope, uint64_t cookie) const {
+ return zx_object_set_cookie(get(), scope.get(), cookie);
+ }
+
+ zx_status_t set_profile(const object<profile>& profile, uint32_t options) const {
+ return zx_object_set_profile(get(), profile.get(), options);
+ }
+
+private:
+ template <typename A, typename B> struct is_same {
+ static const bool value = false;
+ };
+
+ template <typename A> struct is_same<A, A> {
+ static const bool value = true;
+ };
+};
+
+template <typename T> bool operator==(const object<T>& a, const object<T>& b) {
+ return a.get() == b.get();
+}
+
+template <typename T> bool operator!=(const object<T>& a, const object<T>& b) {
+ return !(a == b);
+}
+
+template <typename T> bool operator<(const object<T>& a, const object<T>& b) {
+ return a.get() < b.get();
+}
+
+template <typename T> bool operator>(const object<T>& a, const object<T>& b) {
+ return a.get() > b.get();
+}
+
+template <typename T> bool operator<=(const object<T>& a, const object<T>& b) {
+ return !(a.get() > b.get());
+}
+
+template <typename T> bool operator>=(const object<T>& a, const object<T>& b) {
+ return !(a.get() < b.get());
+}
+
+template <typename T> bool operator==(zx_handle_t a, const object<T>& b) {
+ return a == b.get();
+}
+
+template <typename T> bool operator!=(zx_handle_t a, const object<T>& b) {
+ return !(a == b);
+}
+
+template <typename T> bool operator<(zx_handle_t a, const object<T>& b) {
+ return a < b.get();
+}
+
+template <typename T> bool operator>(zx_handle_t a, const object<T>& b) {
+ return a > b.get();
+}
+
+template <typename T> bool operator<=(zx_handle_t a, const object<T>& b) {
+ return !(a > b.get());
+}
+
+template <typename T> bool operator>=(zx_handle_t a, const object<T>& b) {
+ return !(a < b.get());
+}
+
+template <typename T> bool operator==(const object<T>& a, zx_handle_t b) {
+ return a.get() == b;
+}
+
+template <typename T> bool operator!=(const object<T>& a, zx_handle_t b) {
+ return !(a == b);
+}
+
+template <typename T> bool operator<(const object<T>& a, zx_handle_t b) {
+ return a.get() < b;
+}
+
+template <typename T> bool operator>(const object<T>& a, zx_handle_t b) {
+ return a.get() > b;
+}
+
+template <typename T> bool operator<=(const object<T>& a, zx_handle_t b) {
+ return !(a.get() > b);
+}
+
+template <typename T> bool operator>=(const object<T>& a, zx_handle_t b) {
+ return !(a.get() < b);
+}
+
+// Wraps a handle to an object to provide type-safe access to its operations
+// but does not take ownership of it. The handle is not closed when the
+// wrapper is destroyed.
+//
+// All use of unowned<object<T>> as an object<T> is via a dereference operator,
+// as illustrated below:
+//
+// void do_something(const zx::event& event);
+//
+// void example(zx_handle_t event_handle) {
+// do_something(*zx::unowned<event>(event_handle));
+// }
+//
+// Convenience aliases are provided for all object types, for example:
+//
+// zx::unowned_event(handle)->signal(..)
+template <typename T>
+class unowned final {
+public:
+ explicit unowned(zx_handle_t h) : value_(h) {}
+ explicit unowned(const T& owner) : unowned(owner.get()) {}
+ explicit unowned(unowned& other) : unowned(*other) {}
+ constexpr unowned() = default;
+ unowned(unowned&& other) = default;
+
+ ~unowned() { release_value(); }
+
+ unowned& operator=(unowned& other) {
+ *this = unowned(other);
+ return *this;
+ }
+ unowned& operator=(unowned&& other) {
+ release_value();
+ value_ = static_cast<T&&>(other.value_);
+ return *this;
+ }
+
+ const T& operator*() const { return value_; }
+ const T* operator->() const { return &value_; }
+
+private:
+ void release_value() {
+ zx_handle_t h = value_.release();
+ static_cast<void>(h);
+ }
+
+ T value_;
+};
+
+} // namespace zx
+
+#endif // LIB_ZX_OBJECT_H_
diff --git a/pkg/zx/include/lib/zx/object_traits.h b/pkg/zx/include/lib/zx/object_traits.h
new file mode 100644
index 0000000..f5a62c6
--- /dev/null
+++ b/pkg/zx/include/lib/zx/object_traits.h
@@ -0,0 +1,111 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_OBJECT_TRAITS_H_
+#define LIB_ZX_OBJECT_TRAITS_H_
+
+namespace zx {
+
+class channel;
+class eventpair;
+class log;
+class socket;
+class vmo;
+class vmar;
+class port;
+class guest;
+class fifo;
+class interrupt;
+class pmt;
+
+// The default traits supports:
+// - event
+// - thread
+// - process
+// - job
+// - vmo
+// - bti
+// - resource
+// - timer
+// - iommu
+template <typename T> struct object_traits {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = true;
+ static constexpr bool supports_wait = true;
+ static constexpr bool has_peer_handle = false;
+};
+
+template <> struct object_traits<channel> {
+ static constexpr bool supports_duplication = false;
+ static constexpr bool supports_user_signal = true;
+ static constexpr bool supports_wait = true;
+ static constexpr bool has_peer_handle = true;
+};
+
+template <> struct object_traits<eventpair> {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = true;
+ static constexpr bool supports_wait = true;
+ static constexpr bool has_peer_handle = true;
+};
+
+template <> struct object_traits<fifo> {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = true;
+ static constexpr bool supports_wait = true;
+ static constexpr bool has_peer_handle = true;
+};
+
+template <> struct object_traits<log> {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = true;
+ static constexpr bool supports_wait = true;
+ static constexpr bool has_peer_handle = false;
+};
+
+template <> struct object_traits<pmt> {
+ static constexpr bool supports_duplication = false;
+ static constexpr bool supports_user_signal = false;
+ static constexpr bool supports_wait = false;
+ static constexpr bool has_peer_handle = false;
+};
+
+template <> struct object_traits<socket> {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = true;
+ static constexpr bool supports_wait = true;
+ static constexpr bool has_peer_handle = true;
+};
+
+template <> struct object_traits<port> {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = false;
+ static constexpr bool supports_wait = false;
+ static constexpr bool has_peer_handle = false;
+};
+
+template <> struct object_traits<vmar> {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = false;
+ static constexpr bool supports_wait = false;
+ static constexpr bool has_peer_handle = false;
+};
+
+template <> struct object_traits<interrupt> {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = false;
+ static constexpr bool supports_wait = true;
+ static constexpr bool has_peer_handle = false;
+};
+
+template <> struct object_traits<guest> {
+ static constexpr bool supports_duplication = true;
+ static constexpr bool supports_user_signal = false;
+ static constexpr bool supports_wait = false;
+ static constexpr bool has_peer_handle = false;
+};
+
+} // namespace zx
+
+#endif // LIB_ZX_OBJECT_TRAITS_H_
diff --git a/pkg/zx/include/lib/zx/pmt.h b/pkg/zx/include/lib/zx/pmt.h
new file mode 100644
index 0000000..404235a
--- /dev/null
+++ b/pkg/zx/include/lib/zx/pmt.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef LIB_ZX_PMT_H_
+#define LIB_ZX_PMT_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+namespace zx {
+
+class pmt : public object<pmt> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_PMT;
+
+ constexpr pmt() = default;
+
+ explicit pmt(zx_handle_t value) : object(value) {}
+
+ explicit pmt(handle&& h) : object(h.release()) {}
+
+ pmt(pmt&& other) : object(other.release()) {}
+
+ pmt& operator=(pmt&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ zx_status_t unpin() { return zx_pmt_unpin(release()); }
+};
+
+using unowned_pmt = unowned<pmt>;
+
+} // namespace zx
+
+#endif // LIB_ZX_PMT_H_
diff --git a/pkg/zx/include/lib/zx/port.h b/pkg/zx/include/lib/zx/port.h
new file mode 100644
index 0000000..ef607ec
--- /dev/null
+++ b/pkg/zx/include/lib/zx/port.h
@@ -0,0 +1,50 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_PORT_H_
+#define LIB_ZX_PORT_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <lib/zx/time.h>
+
+namespace zx {
+
+class port : public object<port> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_PORT;
+
+ constexpr port() = default;
+
+ explicit port(zx_handle_t value) : object(value) {}
+
+ explicit port(handle&& h) : object(h.release()) {}
+
+ port(port&& other) : object(other.release()) {}
+
+ port& operator=(port&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint32_t options, port* result);
+
+ zx_status_t queue(const zx_port_packet_t* packet) const {
+ return zx_port_queue(get(), packet);
+ }
+
+ zx_status_t wait(zx::time deadline, zx_port_packet_t* packet) const {
+ return zx_port_wait(get(), deadline.get(), packet);
+ }
+
+ zx_status_t cancel(const object_base& source, uint64_t key) const {
+ return zx_port_cancel(get(), source.get(), key);
+ }
+};
+
+using unowned_port = unowned<port>;
+
+} // namespace zx
+
+#endif // LIB_ZX_PORT_H_
diff --git a/pkg/zx/include/lib/zx/process.h b/pkg/zx/include/lib/zx/process.h
new file mode 100644
index 0000000..c3fe348
--- /dev/null
+++ b/pkg/zx/include/lib/zx/process.h
@@ -0,0 +1,67 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_PROCESS_H_
+#define LIB_ZX_PROCESS_H_
+
+#include <lib/zx/object.h>
+#include <lib/zx/task.h>
+#include <lib/zx/vmar.h>
+#include <lib/zx/vmo.h>
+#include <zircon/process.h>
+
+namespace zx {
+class job;
+class thread;
+
+class process : public task<process> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_PROCESS;
+
+ constexpr process() = default;
+
+ explicit process(zx_handle_t value) : task(value) {}
+
+ explicit process(handle&& h) : task(h.release()) {}
+
+ process(process&& other) : task(other.release()) {}
+
+ process& operator=(process&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ // Rather than creating a process directly with this syscall,
+ // consider using the launchpad library, which properly sets up
+ // the many details of creating a process beyond simply creating
+ // the kernel structure.
+ static zx_status_t create(const job& job, const char* name, uint32_t name_len,
+ uint32_t flags, process* proc, vmar* root_vmar);
+
+ zx_status_t start(const thread& thread_handle, uintptr_t entry,
+ uintptr_t stack, handle arg_handle, uintptr_t arg2) const;
+
+ zx_status_t read_memory(uintptr_t vaddr, void* buffer, size_t len, size_t* actual) const {
+ return zx_process_read_memory(get(), vaddr, buffer, len, actual);
+ }
+
+ zx_status_t write_memory(uintptr_t vaddr, const void* buffer, size_t len, size_t* actual) {
+ return zx_process_write_memory(get(), vaddr, buffer, len, actual);
+ }
+
+ // Provide strongly-typed overload, in addition to get_child(handle*).
+ using task<process>::get_child;
+ zx_status_t get_child(uint64_t koid, zx_rights_t rights,
+ thread* result) const;
+
+ static inline unowned<process> self() {
+ return unowned<process>(zx_process_self());
+ }
+};
+
+using unowned_process = unowned<process>;
+
+} // namespace zx
+
+#endif // LIB_ZX_PROCESS_H_
diff --git a/pkg/zx/include/lib/zx/profile.h b/pkg/zx/include/lib/zx/profile.h
new file mode 100644
index 0000000..ddc3fe3
--- /dev/null
+++ b/pkg/zx/include/lib/zx/profile.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef LIB_ZX_PROFILE_H_
+#define LIB_ZX_PROFILE_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <lib/zx/resource.h>
+
+namespace zx {
+
+class profile : public object<profile> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_LOG;
+
+ constexpr profile() = default;
+
+ explicit profile(zx_handle_t value) : object(value) {}
+
+ explicit profile(handle&& h) : object(h.release()) {}
+
+ profile(profile&& other) : object(other.release()) {}
+
+ profile& operator=(profile&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const resource& resource, const zx_profile_info_t* info, profile* result);
+};
+
+using unowned_profile = unowned<profile>;
+
+} // namespace zx
+
+#endif // LIB_ZX_PROFILE_H_
diff --git a/pkg/zx/include/lib/zx/resource.h b/pkg/zx/include/lib/zx/resource.h
new file mode 100644
index 0000000..4909868
--- /dev/null
+++ b/pkg/zx/include/lib/zx/resource.h
@@ -0,0 +1,43 @@
+// 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.
+
+#ifndef LIB_ZX_RESOURCE_H_
+#define LIB_ZX_RESOURCE_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+namespace zx {
+
+class resource : public object<resource> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_RESOURCE;
+
+ constexpr resource() = default;
+
+ explicit resource(zx_handle_t value) : object(value) {}
+
+ explicit resource(handle&& h) : object(h.release()) {}
+
+ resource(resource&& other) : object(other.release()) {}
+
+ resource& operator=(resource&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const resource& parent,
+ uint32_t options,
+ uint64_t base,
+ size_t len,
+ const char* name,
+ size_t namelen,
+ resource* result);
+};
+
+using unowned_resource = unowned<resource>;
+
+} // namespace zx
+
+#endif // LIB_ZX_RESOURCE_H_
diff --git a/pkg/zx/include/lib/zx/socket.h b/pkg/zx/include/lib/zx/socket.h
new file mode 100644
index 0000000..180e1fa
--- /dev/null
+++ b/pkg/zx/include/lib/zx/socket.h
@@ -0,0 +1,65 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_SOCKET_H_
+#define LIB_ZX_SOCKET_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+namespace zx {
+
+class socket : public object<socket> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_SOCKET;
+
+ constexpr socket() = default;
+
+ explicit socket(zx_handle_t value) : object(value) {}
+
+ explicit socket(handle&& h) : object(h.release()) {}
+
+ socket(socket&& other) : object(other.release()) {}
+
+ socket& operator=(socket&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint32_t options, socket* endpoint0,
+ socket* endpoint1);
+
+ zx_status_t write(uint32_t options, const void* buffer, size_t len,
+ size_t* actual) const {
+ return zx_socket_write(get(), options, buffer, len, actual);
+ }
+
+ zx_status_t read(uint32_t options, void* buffer, size_t len,
+ size_t* actual) const {
+ return zx_socket_read(get(), options, buffer, len, actual);
+ }
+
+ zx_status_t share(socket socket_to_share) const {
+ return zx_socket_share(get(), socket_to_share.release());
+ }
+
+ zx_status_t accept(socket* out_socket) const {
+ // We use a temporary to handle the case where |this| and |out_socket|
+ // are aliased.
+ socket result;
+ zx_status_t status = zx_socket_accept(get(), result.reset_and_get_address());
+ out_socket->reset(result.release());
+ return status;
+ }
+
+ zx_status_t shutdown(uint32_t options) const {
+ return zx_socket_shutdown(get(), options);
+ }
+};
+
+using unowned_socket = unowned<socket>;
+
+} // namespace zx
+
+#endif // LIB_ZX_SOCKET_H_
diff --git a/pkg/zx/include/lib/zx/suspend_token.h b/pkg/zx/include/lib/zx/suspend_token.h
new file mode 100644
index 0000000..8eba2df
--- /dev/null
+++ b/pkg/zx/include/lib/zx/suspend_token.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef LIB_ZX_SUSPEND_TOKEN_H_
+#define LIB_ZX_SUSPEND_TOKEN_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+namespace zx {
+
+// The only thing you can do with a suspend token is close it (which will
+// resume the thread).
+class suspend_token : public object<suspend_token> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_SUSPEND_TOKEN;
+
+ constexpr suspend_token() = default;
+
+ explicit suspend_token(zx_handle_t value) : object<suspend_token>(value) {}
+
+ explicit suspend_token(handle&& h) : object<suspend_token>(h.release()) {}
+
+ suspend_token(suspend_token&& other) : object<suspend_token>(other.release()) {}
+
+ suspend_token& operator=(suspend_token&& other) {
+ reset(other.release());
+ return *this;
+ }
+};
+
+} // namespace zx
+
+#endif // LIB_ZX_SUSPEND_TOKEN_H_
diff --git a/pkg/zx/include/lib/zx/task.h b/pkg/zx/include/lib/zx/task.h
new file mode 100644
index 0000000..a3bc20d
--- /dev/null
+++ b/pkg/zx/include/lib/zx/task.h
@@ -0,0 +1,51 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_TASK_H_
+#define LIB_ZX_TASK_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <lib/zx/suspend_token.h>
+
+namespace zx {
+
+class port;
+class suspend_token;
+
+template <typename T = void> class task : public object<T> {
+public:
+ constexpr task() = default;
+
+ explicit task(zx_handle_t value) : object<T>(value) {}
+
+ explicit task(handle&& h) : object<T>(h.release()) {}
+
+ task(task&& other) : object<T>(other.release()) {}
+
+ zx_status_t bind_exception_port(
+ const object<port>& port, uint64_t key, uint32_t options) const {
+ return zx_task_bind_exception_port(object<T>::get(), port.get(), key, options);
+ }
+
+ zx_status_t kill() const { return zx_task_kill(object<T>::get()); }
+
+ // Deprecated: Use the variant that takes a suspend_token.
+ zx_status_t suspend() const { return zx_task_suspend(object<T>::get()); }
+
+ zx_status_t suspend(suspend_token* result) const {
+ // Assume |result| must refer to a different container than |this|, due
+ // to strict aliasing.
+ return zx_task_suspend_token(
+ object<T>::get(), result->reset_and_get_address());
+ }
+
+ zx_status_t resume_from_exception(const object<port>& port, uint32_t options) const {
+ return zx_task_resume_from_exception(object<T>::get(), port.get(), options);
+ }
+};
+
+} // namespace zx
+
+#endif // LIB_ZX_TASK_H_
diff --git a/pkg/zx/include/lib/zx/thread.h b/pkg/zx/include/lib/zx/thread.h
new file mode 100644
index 0000000..eabf3ed
--- /dev/null
+++ b/pkg/zx/include/lib/zx/thread.h
@@ -0,0 +1,72 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_THREAD_H_
+#define LIB_ZX_THREAD_H_
+
+#include <lib/zx/object.h>
+#include <lib/zx/task.h>
+#include <zircon/process.h>
+
+namespace zx {
+class process;
+
+class thread : public task<thread> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_THREAD;
+
+ constexpr thread() = default;
+
+ explicit thread(zx_handle_t value)
+ : task(value) {}
+
+ explicit thread(handle&& h)
+ : task(h.release()) {}
+
+ thread(thread&& other)
+ : task(other.release()) {}
+
+ thread& operator=(thread&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ // Rather than creating a thread directly with this syscall, consider using
+ // std::thread or thrd_create, which properly integrates with the
+ // thread-local data structures in libc.
+ static zx_status_t create(const process& process, const char* name,
+ uint32_t name_len, uint32_t flags,
+ thread* result);
+
+ // The first variant maps exactly to the syscall and can be used for
+ // launching threads in remote processes. The second variant is for
+ // conveniently launching threads in the current process.
+ zx_status_t start(uintptr_t thread_entry, uintptr_t stack, uintptr_t arg1,
+ uintptr_t arg2) const {
+ return zx_thread_start(get(), thread_entry, stack, arg1, arg2);
+ }
+ zx_status_t start(void (*thread_entry)(uintptr_t arg1, uintptr_t arg2),
+ void* stack, uintptr_t arg1, uintptr_t arg2) {
+ return zx_thread_start(get(),
+ reinterpret_cast<uintptr_t>(thread_entry),
+ reinterpret_cast<uintptr_t>(stack), arg1, arg2);
+ }
+
+ zx_status_t read_state(uint32_t kind, void* buffer, size_t len) const {
+ return zx_thread_read_state(get(), kind, buffer, len);
+ }
+ zx_status_t write_state(uint32_t kind, const void* buffer, size_t len) {
+ return zx_thread_write_state(get(), kind, buffer, len);
+ }
+
+ static inline unowned<thread> self() {
+ return unowned<thread>(zx_thread_self());
+ }
+};
+
+using unowned_thread = unowned<thread>;
+
+} // namespace zx
+
+#endif // LIB_ZX_THREAD_H_
diff --git a/pkg/zx/include/lib/zx/time.h b/pkg/zx/include/lib/zx/time.h
new file mode 100644
index 0000000..101e057
--- /dev/null
+++ b/pkg/zx/include/lib/zx/time.h
@@ -0,0 +1,253 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_TIME_H_
+#define LIB_ZX_TIME_H_
+
+#include <stdint.h>
+#include <zircon/compiler.h>
+#include <zircon/syscalls.h>
+#include <zircon/time.h>
+
+namespace zx {
+
+class duration {
+public:
+ constexpr duration() = default;
+
+ explicit constexpr duration(zx_duration_t value)
+ : value_(value) {}
+
+ static constexpr duration infinite() { return duration(ZX_TIME_INFINITE); }
+
+ static constexpr duration infinite_past() { return duration(ZX_TIME_INFINITE_PAST); }
+
+ constexpr zx_duration_t get() const { return value_; }
+
+ constexpr duration operator+(duration other) const {
+ return duration(zx_duration_add_duration(value_, other.value_));
+ }
+
+ constexpr duration operator-(duration other) const {
+ return duration(zx_duration_sub_duration(value_, other.value_));
+ }
+
+ constexpr duration operator*(int64_t multiplier) const {
+ return duration(zx_duration_mul_int64(value_, multiplier));
+ }
+
+ constexpr duration operator/(int64_t divisor) const {
+ return duration(value_ / divisor);
+ }
+
+ constexpr duration operator%(duration divisor) const {
+ return duration(value_ % divisor.value_);
+ }
+
+ constexpr int64_t operator/(duration other) const {
+ return value_ / other.value_;
+ }
+
+ constexpr duration& operator+=(duration other) {
+ value_ = zx_duration_add_duration(value_, other.value_);
+ return *this;
+ }
+
+ constexpr duration& operator-=(duration other) {
+ value_ = zx_duration_sub_duration(value_, other.value_);
+ return *this;
+ }
+
+ constexpr duration& operator*=(int64_t multiplier) {
+ value_ = zx_duration_mul_int64(value_, multiplier);
+ return *this;
+ }
+
+ constexpr duration& operator/=(int64_t divisor) {
+ value_ /= divisor;
+ return *this;
+ }
+
+ constexpr bool operator==(duration other) const { return value_ == other.value_; }
+ constexpr bool operator!=(duration other) const { return value_ != other.value_; }
+ constexpr bool operator<(duration other) const { return value_ < other.value_; }
+ constexpr bool operator<=(duration other) const { return value_ <= other.value_; }
+ constexpr bool operator>(duration other) const { return value_ > other.value_; }
+ constexpr bool operator>=(duration other) const { return value_ >= other.value_; }
+
+ constexpr int64_t to_nsecs() const { return value_; }
+
+ constexpr int64_t to_usecs() const { return value_ / ZX_USEC(1); }
+
+ constexpr int64_t to_msecs() const { return value_ / ZX_MSEC(1); }
+
+ constexpr int64_t to_secs() const { return value_ / ZX_SEC(1); }
+
+ constexpr int64_t to_mins() const { return value_ / ZX_MIN(1); }
+
+ constexpr int64_t to_hours() const { return value_ / ZX_HOUR(1); }
+
+private:
+ zx_duration_t value_ = 0;
+};
+
+class ticks {
+public:
+ constexpr ticks() = default;
+
+ explicit constexpr ticks(zx_ticks_t value) : value_(value) {}
+
+ // Constructs a tick object for the current tick counter in the system.
+ static ticks now() { return ticks(zx_ticks_get()); }
+
+ // Returns the number of ticks contained within one second.
+ static ticks per_second() { return ticks(zx_ticks_per_second()); }
+
+ // Acquires the number of ticks contained within this object.
+ constexpr zx_ticks_t get() const { return value_; }
+
+ constexpr ticks operator+(ticks other) const {
+ return ticks(value_ + other.value_);
+ }
+
+ constexpr ticks operator-(ticks other) const {
+ return ticks(value_ - other.value_);
+ }
+
+ constexpr ticks operator*(uint64_t multiplier) const {
+ return ticks(value_ * multiplier);
+ }
+
+ constexpr ticks operator/(uint64_t divisor) const {
+ return ticks(value_ / divisor);
+ }
+
+ constexpr uint64_t operator/(ticks other) const {
+ return value_ / other.value_;
+ }
+
+ constexpr ticks& operator+=(ticks other) {
+ value_ += other.value_;
+ return *this;
+ }
+
+ constexpr ticks& operator-=(ticks other) {
+ value_ -= other.value_;
+ return *this;
+ }
+
+ constexpr ticks& operator*=(uint64_t multiplier) {
+ value_ *= multiplier;
+ return *this;
+ }
+
+ constexpr ticks& operator/=(uint64_t divisor) {
+ value_ /= divisor;
+ return *this;
+ }
+
+ constexpr bool operator==(ticks other) const { return value_ == other.value_; }
+ constexpr bool operator!=(ticks other) const { return value_ != other.value_; }
+ constexpr bool operator<(ticks other) const { return value_ < other.value_; }
+ constexpr bool operator<=(ticks other) const { return value_ <= other.value_; }
+ constexpr bool operator>(ticks other) const { return value_ > other.value_; }
+ constexpr bool operator>=(ticks other) const { return value_ >= other.value_; }
+
+private:
+ zx_ticks_t value_ = 0;
+};
+
+template <zx_clock_t kClockId>
+class basic_time {
+public:
+ constexpr basic_time() = default;
+
+ explicit constexpr basic_time(zx_time_t value) : value_(value) {}
+
+ static constexpr basic_time<kClockId> infinite() {
+ return basic_time<kClockId>(ZX_TIME_INFINITE);
+ }
+
+ static constexpr basic_time<kClockId> infinite_past() {
+ return basic_time<kClockId>(ZX_TIME_INFINITE_PAST);
+ }
+
+ constexpr zx_time_t get() const { return value_; }
+
+ zx_time_t* get_address() { return &value_; }
+
+ constexpr duration operator-(basic_time<kClockId> other) const {
+ return duration(zx_time_sub_time(value_, other.value_));
+ }
+
+ constexpr basic_time<kClockId> operator+(duration delta) const {
+ return basic_time<kClockId>(zx_time_add_duration(value_, delta.get()));
+ }
+
+ constexpr basic_time<kClockId> operator-(duration delta) const {
+ return basic_time<kClockId>(zx_time_sub_duration(value_, delta.get()));
+ }
+
+ constexpr basic_time<kClockId>& operator+=(duration delta) {
+ value_ = zx_time_add_duration(value_, delta.get());
+ return *this;
+ }
+
+ constexpr basic_time<kClockId>& operator-=(duration delta) {
+ value_ = zx_time_sub_duration(value_, delta.get());
+ return *this;
+ }
+
+ constexpr bool operator==(basic_time<kClockId> other) const { return value_ == other.value_; }
+ constexpr bool operator!=(basic_time<kClockId> other) const { return value_ != other.value_; }
+ constexpr bool operator<(basic_time<kClockId> other) const { return value_ < other.value_; }
+ constexpr bool operator<=(basic_time<kClockId> other) const { return value_ <= other.value_; }
+ constexpr bool operator>(basic_time<kClockId> other) const { return value_ > other.value_; }
+ constexpr bool operator>=(basic_time<kClockId> other) const { return value_ >= other.value_; }
+
+private:
+ zx_time_t value_ = 0;
+};
+
+using time = basic_time<ZX_CLOCK_MONOTONIC>;
+using time_utc = basic_time<ZX_CLOCK_UTC>;
+using time_thread = basic_time<ZX_CLOCK_THREAD>;
+
+class clock {
+public:
+ clock() = delete;
+
+ template <zx_clock_t kClockId>
+ static zx_status_t get(basic_time<kClockId>* result) {
+ return zx_clock_get_new(kClockId, result->get_address());
+ }
+
+ static time get_monotonic() {
+ return time(zx_clock_get_monotonic());
+ }
+};
+
+constexpr inline duration nsec(int64_t n) { return duration(ZX_NSEC(n)); }
+
+constexpr inline duration usec(int64_t n) { return duration(ZX_USEC(n)); }
+
+constexpr inline duration msec(int64_t n) { return duration(ZX_MSEC(n)); }
+
+constexpr inline duration sec(int64_t n) { return duration(ZX_SEC(n)); }
+
+constexpr inline duration min(int64_t n) { return duration(ZX_MIN(n)); }
+
+constexpr inline duration hour(int64_t n) { return duration(ZX_HOUR(n)); }
+
+inline zx_status_t nanosleep(zx::time deadline) {
+ return zx_nanosleep(deadline.get());
+}
+
+inline time deadline_after(zx::duration nanoseconds) {
+ return time(zx_deadline_after(nanoseconds.get()));
+}
+
+} // namespace zx
+
+#endif // LIB_ZX_TIME_H_
diff --git a/pkg/zx/include/lib/zx/timer.h b/pkg/zx/include/lib/zx/timer.h
new file mode 100644
index 0000000..d7990ac
--- /dev/null
+++ b/pkg/zx/include/lib/zx/timer.h
@@ -0,0 +1,47 @@
+// Copyright 2017 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.
+
+#ifndef LIB_ZX_TIMER_H_
+#define LIB_ZX_TIMER_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+
+#include <zircon/types.h>
+
+namespace zx {
+
+class timer : public object<timer> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_TIMER;
+
+ constexpr timer() = default;
+
+ explicit timer(zx_handle_t value) : object(value) {}
+
+ explicit timer(handle&& h) : object(h.release()) {}
+
+ timer(timer&& other) : object(other.release()) {}
+
+ timer& operator=(timer&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint32_t options, zx_clock_t clock_id, timer* result);
+
+ zx_status_t set(zx::time deadline, zx::duration slack) const {
+ return zx_timer_set(get(), deadline.get(), slack.get());
+ }
+
+ zx_status_t cancel() const {
+ return zx_timer_cancel(get());
+ }
+};
+
+using unowned_timer = unowned<timer>;
+
+} // namespace zx
+
+#endif // LIB_ZX_TIMER_H_
diff --git a/pkg/zx/include/lib/zx/vcpu.h b/pkg/zx/include/lib/zx/vcpu.h
new file mode 100644
index 0000000..dac3f3a
--- /dev/null
+++ b/pkg/zx/include/lib/zx/vcpu.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef LIB_ZX_VCPU_H_
+#define LIB_ZX_VCPU_H_
+
+#include <lib/zx/guest.h>
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <zircon/syscalls/port.h>
+
+namespace zx {
+
+class vcpu : public object<vcpu> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_VCPU;
+
+ constexpr vcpu() = default;
+
+ explicit vcpu(zx_handle_t value) : object(value) {}
+
+ explicit vcpu(handle&& h) : object(h.release()) {}
+
+ vcpu(vcpu&& other) : object(other.release()) {}
+
+ vcpu& operator=(vcpu&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(const guest& guest, uint32_t options,
+ zx_gpaddr_t entry, vcpu* result);
+
+ zx_status_t resume(zx_port_packet_t* packet) {
+ return zx_vcpu_resume(get(), packet);
+ }
+
+ zx_status_t interrupt(uint32_t interrupt) {
+ return zx_vcpu_interrupt(get(), interrupt);
+ }
+
+ zx_status_t read_state(uint32_t kind, void* buf, size_t len) const {
+ return zx_vcpu_read_state(get(), kind, buf, len);
+ }
+
+ zx_status_t write_state(uint32_t kind, const void* buf, size_t len) {
+ return zx_vcpu_write_state(get(), kind, buf, len);
+ }
+};
+
+using unowned_vcpu = unowned<vcpu>;
+
+} // namespace zx
+
+#endif // LIB_ZX_VCPU_H_
diff --git a/pkg/zx/include/lib/zx/vmar.h b/pkg/zx/include/lib/zx/vmar.h
new file mode 100644
index 0000000..1553110
--- /dev/null
+++ b/pkg/zx/include/lib/zx/vmar.h
@@ -0,0 +1,62 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_VMAR_H_
+#define LIB_ZX_VMAR_H_
+
+#include <lib/zx/object.h>
+#include <lib/zx/vmo.h>
+#include <zircon/process.h>
+
+namespace zx {
+
+// A wrapper for handles to VMARs. Note that vmar::~vmar() does not execute
+// vmar::destroy(), it just closes the handle.
+class vmar : public object<vmar> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_VMAR;
+
+ constexpr vmar() = default;
+
+ explicit vmar(zx_handle_t value) : object(value) {}
+
+ explicit vmar(handle&& h) : object(h.release()) {}
+
+ vmar(vmar&& other) : vmar(other.release()) {}
+
+ vmar& operator=(vmar&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ zx_status_t map(size_t vmar_offset, const vmo& vmo_handle, uint64_t vmo_offset,
+ size_t len, zx_vm_option_t options, uintptr_t* ptr) const {
+ return zx_vmar_map(get(), options, vmar_offset, vmo_handle.get(), vmo_offset, len, ptr);
+ }
+
+ zx_status_t unmap(uintptr_t address, size_t len) const {
+ return zx_vmar_unmap(get(), address, len);
+ }
+
+ zx_status_t protect(uintptr_t address, size_t len, zx_vm_option_t prot) const {
+ return zx_vmar_protect(get(), prot, address, len);
+ }
+
+ zx_status_t destroy() const {
+ return zx_vmar_destroy(get());
+ }
+
+ zx_status_t allocate(size_t offset, size_t size, uint32_t flags,
+ vmar* child, uintptr_t* child_addr) const;
+
+ static inline unowned<vmar> root_self() {
+ return unowned<vmar>(zx_vmar_root_self());
+ }
+};
+
+using unowned_vmar = unowned<vmar>;
+
+} // namespace zx
+
+#endif // LIB_ZX_VMAR_H_
diff --git a/pkg/zx/include/lib/zx/vmo.h b/pkg/zx/include/lib/zx/vmo.h
new file mode 100644
index 0000000..82e2cab
--- /dev/null
+++ b/pkg/zx/include/lib/zx/vmo.h
@@ -0,0 +1,89 @@
+// Copyright 2016 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.
+
+#ifndef LIB_ZX_VMO_H_
+#define LIB_ZX_VMO_H_
+
+#include <lib/zx/handle.h>
+#include <lib/zx/object.h>
+#include <lib/zx/resource.h>
+
+namespace zx {
+
+class bti;
+
+class vmo : public object<vmo> {
+public:
+ static constexpr zx_obj_type_t TYPE = ZX_OBJ_TYPE_VMO;
+
+ constexpr vmo() = default;
+
+ explicit vmo(zx_handle_t value) : object(value) {}
+
+ explicit vmo(handle&& h) : object(h.release()) {}
+
+ vmo(vmo&& other) : object(other.release()) {}
+
+ vmo& operator=(vmo&& other) {
+ reset(other.release());
+ return *this;
+ }
+
+ static zx_status_t create(uint64_t size, uint32_t options, vmo* result);
+ static zx_status_t create_contiguous(
+ const bti& bti, size_t size, uint32_t alignment_log2, vmo* result);
+ static zx_status_t create_physical(
+ const resource& resource, zx_paddr_t paddr, size_t size, vmo* result);
+
+ zx_status_t read(void* data, uint64_t offset, size_t len) const {
+ return zx_vmo_read(get(), data, offset, len);
+ }
+
+ zx_status_t write(const void* data, uint64_t offset, size_t len) const {
+ return zx_vmo_write(get(), data, offset, len);
+ }
+
+ zx_status_t get_size(uint64_t* size) const {
+ return zx_vmo_get_size(get(), size);
+ }
+
+ zx_status_t set_size(uint64_t size) const {
+ return zx_vmo_set_size(get(), size);
+ }
+
+ zx_status_t clone(uint32_t options, uint64_t offset, uint64_t size,
+ vmo* result) const {
+ // Allow for the caller aliasing |result| to |this|.
+ vmo h;
+ zx_status_t status = zx_vmo_clone(
+ get(), options, offset, size, h.reset_and_get_address());
+ result->reset(h.release());
+ return status;
+ }
+
+ zx_status_t op_range(uint32_t op, uint64_t offset, uint64_t size,
+ void* buffer, size_t buffer_size) const {
+ return zx_vmo_op_range(get(), op, offset, size, buffer, buffer_size);
+ }
+
+ zx_status_t set_cache_policy(uint32_t cache_policy) {
+ return zx_vmo_set_cache_policy(get(), cache_policy);
+ }
+
+ zx_status_t replace_as_executable(const handle& vmex, vmo* result) {
+ zx_handle_t h = ZX_HANDLE_INVALID;
+ zx_status_t status = zx_vmo_replace_as_executable(value_, vmex.get(), &h);
+ // We store ZX_HANDLE_INVALID to value_ before calling reset on result
+ // in case result == this.
+ value_ = ZX_HANDLE_INVALID;
+ result->reset(h);
+ return status;
+ }
+};
+
+using unowned_vmo = unowned<vmo>;
+
+} // namespace zx
+
+#endif // LIB_ZX_VMO_H_
diff --git a/pkg/zx/interrupt.cpp b/pkg/zx/interrupt.cpp
new file mode 100644
index 0000000..609406d
--- /dev/null
+++ b/pkg/zx/interrupt.cpp
@@ -0,0 +1,19 @@
+// 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/zx/interrupt.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t interrupt::create(const resource& resource, uint32_t vector,
+ uint32_t options, interrupt* result) {
+ // Assume |result| uses a distinct container from |resource|, due to
+ // strict aliasing.
+ return zx_interrupt_create(
+ resource.get(), vector, options, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/iommu.cpp b/pkg/zx/iommu.cpp
new file mode 100644
index 0000000..ad3c078
--- /dev/null
+++ b/pkg/zx/iommu.cpp
@@ -0,0 +1,16 @@
+// 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/zx/iommu.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t iommu::create(const resource& resource, uint32_t type, const void* desc,
+ size_t desc_size, iommu* result) {
+ return zx_iommu_create(resource.get(), type, desc, desc_size, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/job.cpp b/pkg/zx/job.cpp
new file mode 100644
index 0000000..6989589
--- /dev/null
+++ b/pkg/zx/job.cpp
@@ -0,0 +1,29 @@
+// Copyright 2016 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/zx/job.h>
+
+#include <lib/zx/process.h>
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t job::create(const job& parent, uint32_t flags, job* result) {
+ // Allow for aliasing of the same container to |result| and |parent|.
+ job h;
+ zx_status_t status =
+ zx_job_create(parent.get(), flags, h.reset_and_get_address());
+ result->reset(h.release());
+ return status;
+}
+
+zx_status_t job::get_child(uint64_t koid, zx_rights_t rights,
+ process* result) const {
+ // Assume |result| and |this| are distinct containers, due to strict
+ // aliasing.
+ return zx_object_get_child(
+ value_, koid, rights, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/log.cpp b/pkg/zx/log.cpp
new file mode 100644
index 0000000..4c06b36
--- /dev/null
+++ b/pkg/zx/log.cpp
@@ -0,0 +1,15 @@
+// Copyright 2016 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/zx/log.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t log::create(uint32_t flags, log* result) {
+ return zx_debuglog_create(ZX_HANDLE_INVALID, flags, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/meta.json b/pkg/zx/meta.json
new file mode 100644
index 0000000..27622e6
--- /dev/null
+++ b/pkg/zx/meta.json
@@ -0,0 +1,113 @@
+{
+ "banjo_deps": [],
+ "deps": [],
+ "fidl_deps": [],
+ "files": [
+ "pkg/zx/bti.cpp",
+ "pkg/zx/channel.cpp",
+ "pkg/zx/debuglog.cpp",
+ "pkg/zx/event.cpp",
+ "pkg/zx/eventpair.cpp",
+ "pkg/zx/fifo.cpp",
+ "pkg/zx/guest.cpp",
+ "pkg/zx/interrupt.cpp",
+ "pkg/zx/iommu.cpp",
+ "pkg/zx/job.cpp",
+ "pkg/zx/log.cpp",
+ "pkg/zx/port.cpp",
+ "pkg/zx/process.cpp",
+ "pkg/zx/profile.cpp",
+ "pkg/zx/resource.cpp",
+ "pkg/zx/socket.cpp",
+ "pkg/zx/thread.cpp",
+ "pkg/zx/timer.cpp",
+ "pkg/zx/vcpu.cpp",
+ "pkg/zx/vmar.cpp",
+ "pkg/zx/vmo.cpp",
+ "pkg/zx/include/lib/zx/bti.h",
+ "pkg/zx/include/lib/zx/channel.h",
+ "pkg/zx/include/lib/zx/debuglog.h",
+ "pkg/zx/include/lib/zx/event.h",
+ "pkg/zx/include/lib/zx/eventpair.h",
+ "pkg/zx/include/lib/zx/fifo.h",
+ "pkg/zx/include/lib/zx/guest.h",
+ "pkg/zx/include/lib/zx/handle.h",
+ "pkg/zx/include/lib/zx/interrupt.h",
+ "pkg/zx/include/lib/zx/iommu.h",
+ "pkg/zx/include/lib/zx/job.h",
+ "pkg/zx/include/lib/zx/log.h",
+ "pkg/zx/include/lib/zx/object.h",
+ "pkg/zx/include/lib/zx/object_traits.h",
+ "pkg/zx/include/lib/zx/pmt.h",
+ "pkg/zx/include/lib/zx/port.h",
+ "pkg/zx/include/lib/zx/process.h",
+ "pkg/zx/include/lib/zx/profile.h",
+ "pkg/zx/include/lib/zx/resource.h",
+ "pkg/zx/include/lib/zx/socket.h",
+ "pkg/zx/include/lib/zx/suspend_token.h",
+ "pkg/zx/include/lib/zx/task.h",
+ "pkg/zx/include/lib/zx/thread.h",
+ "pkg/zx/include/lib/zx/time.h",
+ "pkg/zx/include/lib/zx/timer.h",
+ "pkg/zx/include/lib/zx/vcpu.h",
+ "pkg/zx/include/lib/zx/vmar.h",
+ "pkg/zx/include/lib/zx/vmo.h"
+ ],
+ "headers": [
+ "pkg/zx/include/lib/zx/bti.h",
+ "pkg/zx/include/lib/zx/channel.h",
+ "pkg/zx/include/lib/zx/debuglog.h",
+ "pkg/zx/include/lib/zx/event.h",
+ "pkg/zx/include/lib/zx/eventpair.h",
+ "pkg/zx/include/lib/zx/fifo.h",
+ "pkg/zx/include/lib/zx/guest.h",
+ "pkg/zx/include/lib/zx/handle.h",
+ "pkg/zx/include/lib/zx/interrupt.h",
+ "pkg/zx/include/lib/zx/iommu.h",
+ "pkg/zx/include/lib/zx/job.h",
+ "pkg/zx/include/lib/zx/log.h",
+ "pkg/zx/include/lib/zx/object.h",
+ "pkg/zx/include/lib/zx/object_traits.h",
+ "pkg/zx/include/lib/zx/pmt.h",
+ "pkg/zx/include/lib/zx/port.h",
+ "pkg/zx/include/lib/zx/process.h",
+ "pkg/zx/include/lib/zx/profile.h",
+ "pkg/zx/include/lib/zx/resource.h",
+ "pkg/zx/include/lib/zx/socket.h",
+ "pkg/zx/include/lib/zx/suspend_token.h",
+ "pkg/zx/include/lib/zx/task.h",
+ "pkg/zx/include/lib/zx/thread.h",
+ "pkg/zx/include/lib/zx/time.h",
+ "pkg/zx/include/lib/zx/timer.h",
+ "pkg/zx/include/lib/zx/vcpu.h",
+ "pkg/zx/include/lib/zx/vmar.h",
+ "pkg/zx/include/lib/zx/vmo.h"
+ ],
+ "include_dir": "pkg/zx/include",
+ "name": "zx",
+ "root": "pkg/zx",
+ "sources": [
+ "pkg/zx/bti.cpp",
+ "pkg/zx/channel.cpp",
+ "pkg/zx/debuglog.cpp",
+ "pkg/zx/event.cpp",
+ "pkg/zx/eventpair.cpp",
+ "pkg/zx/fifo.cpp",
+ "pkg/zx/guest.cpp",
+ "pkg/zx/interrupt.cpp",
+ "pkg/zx/iommu.cpp",
+ "pkg/zx/job.cpp",
+ "pkg/zx/log.cpp",
+ "pkg/zx/port.cpp",
+ "pkg/zx/process.cpp",
+ "pkg/zx/profile.cpp",
+ "pkg/zx/resource.cpp",
+ "pkg/zx/socket.cpp",
+ "pkg/zx/thread.cpp",
+ "pkg/zx/timer.cpp",
+ "pkg/zx/vcpu.cpp",
+ "pkg/zx/vmar.cpp",
+ "pkg/zx/vmo.cpp"
+ ],
+ "type": "cc_source_library"
+}
\ No newline at end of file
diff --git a/pkg/zx/port.cpp b/pkg/zx/port.cpp
new file mode 100644
index 0000000..f6ca294
--- /dev/null
+++ b/pkg/zx/port.cpp
@@ -0,0 +1,15 @@
+// Copyright 2016 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/zx/port.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t port::create(uint32_t options, port* result) {
+ return zx_port_create(options, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/process.cpp b/pkg/zx/process.cpp
new file mode 100644
index 0000000..c47fa79
--- /dev/null
+++ b/pkg/zx/process.cpp
@@ -0,0 +1,38 @@
+// Copyright 2016 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/zx/process.h>
+
+#include <zircon/syscalls.h>
+
+#include <lib/zx/job.h>
+#include <lib/zx/thread.h>
+#include <lib/zx/vmar.h>
+
+namespace zx {
+
+zx_status_t process::create(const job& job, const char* name, uint32_t name_len,
+ uint32_t flags, process* proc, vmar* vmar) {
+ // Assume |proc|, |vmar| and |job| must refer to different containers, due
+ // to strict aliasing.
+ return zx_process_create(
+ job.get(), name, name_len, flags, proc->reset_and_get_address(),
+ vmar->reset_and_get_address());
+}
+
+zx_status_t process::start(const thread& thread_handle, uintptr_t entry,
+ uintptr_t stack, handle arg_handle,
+ uintptr_t arg2) const {
+ return zx_process_start(get(), thread_handle.get(), entry, stack, arg_handle.release(), arg2);
+}
+
+zx_status_t process::get_child(uint64_t koid, zx_rights_t rights,
+ thread* result) const {
+ // Assume |result| and |this| are distinct containers, due to strict
+ // aliasing.
+ return zx_object_get_child(
+ value_, koid, rights, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/profile.cpp b/pkg/zx/profile.cpp
new file mode 100644
index 0000000..9e67647
--- /dev/null
+++ b/pkg/zx/profile.cpp
@@ -0,0 +1,16 @@
+// 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/zx/profile.h>
+
+#include <zircon/syscalls.h>
+#include <zircon/syscalls/profile.h>
+
+namespace zx {
+
+zx_status_t profile::create(const resource& resource, const zx_profile_info_t* info, profile* result) {
+ return zx_profile_create(resource.get(), info, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/resource.cpp b/pkg/zx/resource.cpp
new file mode 100644
index 0000000..8f29841
--- /dev/null
+++ b/pkg/zx/resource.cpp
@@ -0,0 +1,25 @@
+// 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/zx/resource.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t resource::create(const resource& parent,
+ uint32_t options,
+ uint64_t base,
+ size_t len,
+ const char* name,
+ size_t namelen,
+ resource* result) {
+ resource h;
+ zx_status_t status = zx_resource_create(parent.get(), options, base, len, name, namelen,
+ h.reset_and_get_address());
+ result->reset(h.release());
+ return status;
+}
+
+} // namespace zx
diff --git a/pkg/zx/socket.cpp b/pkg/zx/socket.cpp
new file mode 100644
index 0000000..76f5071
--- /dev/null
+++ b/pkg/zx/socket.cpp
@@ -0,0 +1,25 @@
+// Copyright 2016 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/zx/socket.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t socket::create(uint32_t flags, socket* endpoint0,
+ socket* endpoint1) {
+ // Ensure aliasing of both out parameters to the same container
+ // has a well-defined result, and does not leak.
+ socket h0;
+ socket h1;
+ zx_status_t status = zx_socket_create(
+ flags, h0.reset_and_get_address(),
+ h1.reset_and_get_address());
+ endpoint0->reset(h0.release());
+ endpoint1->reset(h1.release());
+ return status;
+}
+
+} // namespace zx
diff --git a/pkg/zx/thread.cpp b/pkg/zx/thread.cpp
new file mode 100644
index 0000000..4b9ab4c
--- /dev/null
+++ b/pkg/zx/thread.cpp
@@ -0,0 +1,21 @@
+// Copyright 2016 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/zx/thread.h>
+
+#include <zircon/syscalls.h>
+
+#include <lib/zx/process.h>
+
+namespace zx {
+
+zx_status_t thread::create(const process& process, const char* name,
+ uint32_t name_len, uint32_t flags, thread* result) {
+ // Assume |result| and |process| must refer to different containers, due
+ // to strict aliasing.
+ return zx_thread_create(
+ process.get(), name, name_len, flags, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/timer.cpp b/pkg/zx/timer.cpp
new file mode 100644
index 0000000..f67ddc3
--- /dev/null
+++ b/pkg/zx/timer.cpp
@@ -0,0 +1,15 @@
+// Copyright 2017 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/zx/timer.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t timer::create(uint32_t options, zx_clock_t clock_id, timer* result) {
+ return zx_timer_create(options, clock_id, result->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/vcpu.cpp b/pkg/zx/vcpu.cpp
new file mode 100644
index 0000000..5584a5e
--- /dev/null
+++ b/pkg/zx/vcpu.cpp
@@ -0,0 +1,19 @@
+// 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/zx/vcpu.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t vcpu::create(const guest& guest, uint32_t options,
+ zx_gpaddr_t entry, vcpu* vcpu) {
+ // Assume |guest| and |vcpu| must refer to different containers, due to
+ // strict aliasing.
+ return zx_vcpu_create(guest.get(), options, entry,
+ vcpu->reset_and_get_address());
+}
+
+} // namespace zx
diff --git a/pkg/zx/vmar.cpp b/pkg/zx/vmar.cpp
new file mode 100644
index 0000000..60148f2
--- /dev/null
+++ b/pkg/zx/vmar.cpp
@@ -0,0 +1,21 @@
+// Copyright 2016 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/zx/vmar.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t vmar::allocate(size_t offset, size_t size, zx_vm_option_t options,
+ vmar* child, uintptr_t* child_addr) const {
+ // Allow for aliasing of |child| to the same container as |this|.
+ vmar h;
+ zx_status_t status = zx_vmar_allocate(
+ get(), options, offset, size, h.reset_and_get_address(), child_addr);
+ child->reset(h.release());
+ return status;
+}
+
+} // namespace zx
diff --git a/pkg/zx/vmo.cpp b/pkg/zx/vmo.cpp
new file mode 100644
index 0000000..130c71f
--- /dev/null
+++ b/pkg/zx/vmo.cpp
@@ -0,0 +1,27 @@
+// Copyright 2016 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/zx/vmo.h>
+#include <lib/zx/bti.h>
+
+#include <zircon/syscalls.h>
+
+namespace zx {
+
+zx_status_t vmo::create(uint64_t size, uint32_t options, vmo* result) {
+ return zx_vmo_create(size, options, result->reset_and_get_address());
+}
+
+zx_status_t vmo::create_contiguous(
+ const bti& bti, size_t size, uint32_t alignment_log2, vmo* result) {
+ return zx_vmo_create_contiguous(bti.get(), size, alignment_log2,
+ result->reset_and_get_address());
+}
+
+zx_status_t vmo::create_physical(
+ const resource& resource, zx_paddr_t paddr, size_t size, vmo* result) {
+ return zx_vmo_create_physical(resource.get(), paddr, size, result->reset_and_get_address());
+}
+
+} // namespace zx