text: // SPDX-License-Identifier: BSD-3-Clause
  /* Copyright 2022, Intel Corporation */
  
  #include "test_helpers.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <libminiasync.h>
  
  struct alloc_data {
  	size_t n;
  };
  
  struct alloc_output {
  	void *ptr;
  };
  
  FUTURE(alloc_fut, struct alloc_data, struct alloc_output);
  
  enum future_state
  alloc_impl(struct future_context *context, struct future_notifier *notifier)
  {
  	struct alloc_data *data = future_context_get_data(context);
  	struct alloc_output *output = future_context_get_output(context);
  
  	output->ptr = malloc(data->n);
  	UT_ASSERTne(output->ptr, NULL);
  
  	return FUTURE_STATE_COMPLETE;
  }
  
  struct alloc_fut
  async_alloc(size_t size)
  {
  	struct alloc_fut fut = {.output.ptr = NULL};
  	fut.data.n = size;
  	FUTURE_INIT(&fut, alloc_impl);
  	return fut;
  };
  
  struct strdup_data {
  	FUTURE_CHAIN_ENTRY(struct alloc_fut, alloc);
  	FUTURE_CHAIN_ENTRY_LAST(struct vdm_operation_future, copy);
  	void *src;
  	size_t length;
  };
  
  struct strdup_output {
  	void *ptr;
  	size_t length;
  };
  
  FUTURE(strdup_fut, struct strdup_data, struct strdup_output);
  
  void
  strdup_map_alloc_to_copy(struct future_context *lhs,
  	struct future_context *rhs, void *arg)
  {
  	struct alloc_output *alloc = future_context_get_output(lhs);
  	struct vdm_operation_data *copy = future_context_get_data(rhs);
  	copy->operation.data.memcpy.dest = alloc->ptr;
  }
  
  void
  strdup_map_copy_to_output(struct future_context *lhs,
  	struct future_context *rhs, void *arg)
  {
  	struct vdm_operation_data *copy = future_context_get_data(lhs);
  	struct vdm_operation_output *copy_output =
  		future_context_get_output(lhs);
  	UT_ASSERTeq(copy_output->result, VDM_SUCCESS);
  	struct strdup_output *strdup = future_context_get_output(rhs);
  	strdup->ptr = copy->operation.data.memcpy.dest;
  	strdup->length = copy->operation.data.memcpy.n;
  }
  
  struct strdup_fut
  async_strdup(struct vdm *vdm, char *s)
  {
  	struct strdup_fut fut = {.output = {.length = 0, .ptr = NULL}};
  
  	size_t len = strlen(s) + 1;
  	FUTURE_CHAIN_ENTRY_INIT(&fut.data.alloc, async_alloc(len),
  		strdup_map_alloc_to_copy, NULL);
  	FUTURE_CHAIN_ENTRY_INIT(&fut.data.copy,
  		vdm_memcpy(vdm, NULL, s, len, 0),
  		strdup_map_copy_to_output, NULL);
  	FUTURE_CHAIN_INIT(&fut);
  
  	return fut;
  }
  
  void
  strdup_init(void *future, struct future_context *chain_fut, void *arg)
  {
  	struct vdm *vdm = arg;
  	struct strdup_data *strdup_data =
  		future_context_get_data(chain_fut);
  
  	struct vdm_operation_future fut = vdm_memcpy(vdm,
  		strdup_data->alloc.fut.output.ptr,
  		strdup_data->src, strdup_data->length, 0);
  	memcpy(future, &fut, sizeof(fut));
  }
  
  struct strdup_fut
  async_lazy_strdup(struct vdm *vdm, char *s)
  {
  	struct strdup_fut fut = {.output = {.length = 0, .ptr = NULL}};
  	fut.data.src = s;
  	fut.data.length = strlen(s) + 1;
  
  	FUTURE_CHAIN_ENTRY_INIT(&fut.data.alloc, async_alloc(fut.data.length),
  		strdup_map_alloc_to_copy, NULL);
  	FUTURE_CHAIN_ENTRY_LAZY_INIT(&fut.data.copy,
  		strdup_init, vdm,
  		strdup_map_copy_to_output, NULL);
  	FUTURE_CHAIN_INIT(&fut);
  
  	return fut;
  }
  
  static char *hello_world = "Hello World!";
  
  void
  test_strdup_fut(struct strdup_fut fut)
  {
  	FUTURE_BUSY_POLL(&fut);
  
  	struct strdup_output *output = FUTURE_OUTPUT(&fut);
  	UT_ASSERTeq(strcmp(hello_world, output->ptr), 0);
  	UT_ASSERTeq(strlen(hello_world) + 1, output->length);
  
  	free(output->ptr);
  }
  
  void
  test_too_many_ops(struct vdm *vdm)
  {
  	struct vdm_operation_future first_op;
  	int assigned = 0;
  
  	enum future_state state;
  	char dest = 0;
  	char src = 1;
  	do {
  		struct vdm_operation_future f =
  			vdm_memcpy(vdm, &dest, &src, 1, 0);
  		if (!assigned) {
  			assigned = 1;
  			first_op = f;
  		}
  		state = FUTURE_STATE(&f);
  		if (state == FUTURE_STATE_COMPLETE) {
  			struct vdm_operation_output *output = FUTURE_OUTPUT(&f);
  			UT_ASSERTeq(output->result, VDM_ERROR_OUT_OF_MEMORY);
  		}
  	} while (state != FUTURE_STATE_COMPLETE);
  	FUTURE_BUSY_POLL(&first_op);
  	UT_ASSERTeq(dest, src);
  
  	struct vdm_operation_future f = vdm_memcpy(vdm, &dest, &src, 1, 0);
  	UT_ASSERTeq(FUTURE_STATE(&f), FUTURE_STATE_IDLE);
  	FUTURE_BUSY_POLL(&f);
  	UT_ASSERTeq(FUTURE_OUTPUT(&f)->result, VDM_SUCCESS);
  }
  
  int
  main(void)
  {
  	UT_ASSERTeq(sizeof(struct vdm_operation_data), 64);
  
  	struct data_mover_sync *sync = data_mover_sync_new();
  	struct vdm *vdm = data_mover_sync_get_vdm(sync);
  
  	test_strdup_fut(async_strdup(vdm, hello_world));
  	test_strdup_fut(async_lazy_strdup(vdm, hello_world));
  	test_too_many_ops(vdm);
  
  	data_mover_sync_delete(sync);
  
  	return 0;
  }
  