OverviewFilesCommitsIssuesDocsReleasesPeers

ollama_semantic_function_calling / ollamatools-new / keet / node_modules / udx-native / binding.c

2052 lines · c ref:
#include <node_api.h>
#include <stdlib.h>
#include <string.h>
#include <udx.h>
#include <uv.h>

#define UDX_NAPI_INTERACTIVE     0
#define UDX_NAPI_NON_INTERACTIVE 1
#define UDX_NAPI_FRAMED          2

typedef struct {
  udx_t udx;

  char *read_buf;
  size_t read_buf_free;

  napi_async_cleanup_hook_handle teardown;
  bool exiting;
  bool has_teardown;
} udx_napi_t;

typedef struct {
  udx_socket_t socket;
  udx_napi_t *udx;

  napi_env env;
  napi_ref ctx;
  napi_ref on_send;
  napi_ref on_message;
  napi_ref on_close;
  napi_ref realloc_message;
} udx_napi_socket_t;

typedef struct {
  udx_stream_t stream;
  udx_napi_t *udx;

  int mode;

  char *read_buf;
  char *read_buf_head;
  size_t read_buf_free;

  ssize_t frame_len;

  napi_env env;
  napi_ref ctx;
  napi_ref on_data;
  napi_ref on_end;
  napi_ref on_drain;
  napi_ref on_ack;
  napi_ref on_send;
  napi_ref on_message;
  napi_ref on_close;
  napi_ref on_firewall;
  napi_ref on_remote_changed;
  napi_ref realloc_data;
  napi_ref realloc_message;
} udx_napi_stream_t;

typedef struct {
  udx_lookup_t handle;
  udx_napi_t *udx;

  char *host;

  napi_env env;
  napi_ref ctx;
  napi_ref on_lookup;
} udx_napi_lookup_t;

typedef struct {
  udx_interface_event_t handle;
  udx_napi_t *udx;

  napi_env env;
  napi_ref ctx;
  napi_ref on_event;
  napi_ref on_close;
} udx_napi_interface_event_t;

inline static void
parse_address (struct sockaddr *name, char *ip, size_t size, int *port, int *family) {
  if (name->sa_family == AF_INET) {
    *port = ntohs(((struct sockaddr_in *) name)->sin_port);
    *family = 4;
    uv_ip4_name((struct sockaddr_in *) name, ip, size);
  } else if (name->sa_family == AF_INET6) {
    *port = ntohs(((struct sockaddr_in6 *) name)->sin6_port);
    *family = 6;
    uv_ip6_name((struct sockaddr_in6 *) name, ip, size);
  }
}

static void
on_udx_send (udx_socket_send_t *req, int status) {
  udx_napi_socket_t *n = (udx_napi_socket_t *) req->socket;
  if (n->udx->exiting) return;

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_send, &callback);

  napi_value argv[2];
  napi_create_int32(env, (uintptr_t) req->data, &(argv[0]));
  napi_create_int32(env, status, &(argv[1]));

  if (napi_make_callback(env, NULL, ctx, callback, 2, argv, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_message (udx_socket_t *self, ssize_t read_len, const uv_buf_t *buf, const struct sockaddr *from) {
  udx_napi_socket_t *n = (udx_napi_socket_t *) self;
  if (n->udx->exiting) return;

  int port = 0;
  char ip[INET6_ADDRSTRLEN];
  int family = 0;
  parse_address((struct sockaddr *) from, ip, INET6_ADDRSTRLEN, &port, &family);

  if (buf->len > n->udx->read_buf_free) return;

  memcpy(n->udx->read_buf, buf->base, buf->len);

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_message, &callback);

  napi_value argv[4];
  napi_create_uint32(env, read_len, &(argv[0]));
  napi_create_uint32(env, port, &(argv[1]));
  napi_create_string_utf8(env, ip, NAPI_AUTO_LENGTH, &(argv[2]));
  napi_create_uint32(env, family, &(argv[3]));

  napi_value res;

  if (napi_make_callback(env, NULL, ctx, callback, 4, argv, &res) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);

    // avoid reentry
    if (!(n->udx->exiting)) {
      napi_env env = n->env;

      napi_handle_scope scope;
      napi_open_handle_scope(env, &scope);

      napi_value ctx;
      napi_get_reference_value(env, n->ctx, &ctx);

      napi_value callback;
      napi_get_reference_value(env, n->realloc_message, &callback);

      if (napi_make_callback(env, NULL, ctx, callback, 0, NULL, &res) == napi_pending_exception) {
        napi_value fatal_exception;
        napi_get_and_clear_last_exception(env, &fatal_exception);
        napi_fatal_exception(env, fatal_exception);
      }

      napi_get_buffer_info(env, res, (void **) &(n->udx->read_buf), &(n->udx->read_buf_free));

      napi_close_handle_scope(env, scope);
    }
  } else {
    napi_get_buffer_info(env, res, (void **) &(n->udx->read_buf), &(n->udx->read_buf_free));
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_close (udx_socket_t *self) {
  udx_napi_socket_t *n = (udx_napi_socket_t *) self;

  napi_env env = n->env;

  if (!(n->udx->exiting)) {
    napi_handle_scope scope;
    napi_open_handle_scope(env, &scope);

    napi_value ctx;
    napi_get_reference_value(env, n->ctx, &ctx);

    napi_value callback;
    napi_get_reference_value(env, n->on_close, &callback);

    if (napi_make_callback(env, NULL, ctx, callback, 0, NULL, NULL) == napi_pending_exception) {
      napi_value fatal_exception;
      napi_get_and_clear_last_exception(env, &fatal_exception);
      napi_fatal_exception(env, fatal_exception);
    }

    napi_close_handle_scope(env, scope);
  }

  napi_delete_reference(env, n->on_send);
  napi_delete_reference(env, n->on_message);
  napi_delete_reference(env, n->on_close);
  napi_delete_reference(env, n->realloc_message);
  napi_delete_reference(env, n->ctx);
}

static void
on_udx_teardown (napi_async_cleanup_hook_handle handle, void *data) {
  udx_napi_t *self = (udx_napi_t *) data;
  udx_t *udx = (udx_t *) data;

  self->exiting = true;
  udx_teardown(udx);
}

static void
ensure_teardown (napi_env env, udx_napi_t *udx) {
  if (udx->has_teardown) return;
  udx->has_teardown = true;
  napi_add_async_cleanup_hook(env, on_udx_teardown, (void *) udx, &(udx->teardown));
}

static void
on_udx_stream_end (udx_stream_t *stream) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) stream;
  if (n->udx->exiting) return;

  size_t read = n->read_buf_head - n->read_buf;

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_end, &callback);

  napi_value argv[1];
  napi_create_uint32(env, read, &(argv[0]));

  if (napi_make_callback(env, NULL, ctx, callback, 1, argv, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_stream_read (udx_stream_t *stream, ssize_t read_len, const uv_buf_t *buf) {
  if (read_len == UV_EOF) return on_udx_stream_end(stream);

  udx_napi_stream_t *n = (udx_napi_stream_t *) stream;
  if (n->udx->exiting) return;

  // ignore the message if it doesn't fit in the read buffer
  if (buf->len > n->read_buf_free) return;

  if (n->mode == UDX_NAPI_FRAMED && n->frame_len == -1) {
    if (buf->len < 3) {
      n->mode = UDX_NAPI_INTERACTIVE;
    } else {
      uint8_t *b = (uint8_t *) buf->base;
      n->frame_len = 3 + (b[0] | (b[1] << 8) | (b[2] << 16));
    }
  }

  memcpy(n->read_buf_head, buf->base, buf->len);

  n->read_buf_head += buf->len;
  n->read_buf_free -= buf->len;

  if (n->mode == UDX_NAPI_NON_INTERACTIVE && n->read_buf_free >= 2 * stream->mtu) {
    return;
  }

  ssize_t read = n->read_buf_head - n->read_buf;

  if (n->mode == UDX_NAPI_FRAMED) {
    if (n->frame_len < read) {
      n->mode = UDX_NAPI_INTERACTIVE;
    } else if (n->frame_len == read) {
      n->frame_len = -1;
    } else if (n->read_buf_free < 2 * stream->mtu) {
      n->frame_len -= read;
    } else {
      return; // wait for more data
    }
  }

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_data, &callback);

  napi_value argv[1];
  napi_create_uint32(env, read, &(argv[0]));

  napi_value res;

  if (napi_make_callback(env, NULL, ctx, callback, 1, argv, &res) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);

    // avoid re-entry
    if (!(n->udx->exiting)) {
      napi_handle_scope scope;
      napi_open_handle_scope(env, &scope);

      napi_value ctx;
      napi_get_reference_value(env, n->ctx, &ctx);

      napi_value callback;
      napi_get_reference_value(env, n->realloc_data, &callback);

      if (napi_make_callback(env, NULL, ctx, callback, 0, NULL, &res) == napi_pending_exception) {
        napi_value fatal_exception;
        napi_get_and_clear_last_exception(env, &fatal_exception);
        napi_fatal_exception(env, fatal_exception);
      }

      napi_get_buffer_info(env, res, (void **) &(n->read_buf), &(n->read_buf_free));
      n->read_buf_head = n->read_buf;

      napi_close_handle_scope(env, scope);
    }
  } else {
    napi_get_buffer_info(env, res, (void **) &(n->read_buf), &(n->read_buf_free));
    n->read_buf_head = n->read_buf;
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_stream_drain (udx_stream_t *stream) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) stream;
  if (n->udx->exiting) return;

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_drain, &callback);

  if (napi_make_callback(env, NULL, ctx, callback, 0, NULL, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_stream_ack (udx_stream_write_t *req, int status, int unordered) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) req->stream;
  if (n->udx->exiting) return;

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_ack, &callback);

  napi_value argv[1];
  napi_create_uint32(env, (uintptr_t) req->data, &(argv[0]));

  if (napi_make_callback(env, NULL, ctx, callback, 1, argv, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_stream_send (udx_stream_send_t *req, int status) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) req->stream;
  if (n->udx->exiting) return;

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_send, &callback);

  napi_value argv[2];
  napi_create_int32(env, (uintptr_t) req->data, &(argv[0]));
  napi_create_int32(env, status, &(argv[1]));

  if (napi_make_callback(env, NULL, ctx, callback, 2, argv, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_stream_recv (udx_stream_t *stream, ssize_t read_len, const uv_buf_t *buf) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) stream;
  if (n->udx->exiting) return;

  if (buf->len > n->udx->read_buf_free) return;

  memcpy(n->udx->read_buf, buf->base, buf->len);

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_message, &callback);

  napi_value argv[1];
  napi_create_uint32(env, read_len, &(argv[0]));

  napi_value res;

  if (napi_make_callback(env, NULL, ctx, callback, 1, argv, &res) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);

    // avoid re-entry
    if (!(n->udx->exiting)) {
      napi_handle_scope scope;
      napi_open_handle_scope(env, &scope);

      napi_value ctx;
      napi_get_reference_value(env, n->ctx, &ctx);

      napi_value callback;
      napi_get_reference_value(env, n->realloc_message, &callback);

      if (napi_make_callback(env, NULL, ctx, callback, 0, NULL, &res) == napi_pending_exception) {
        napi_value fatal_exception;
        napi_get_and_clear_last_exception(env, &fatal_exception);
        napi_fatal_exception(env, fatal_exception);
      }

      napi_get_buffer_info(env, res, (void **) &(n->udx->read_buf), &(n->udx->read_buf_free));

      napi_close_handle_scope(env, scope);
    }
  } else {
    napi_get_buffer_info(env, res, (void **) &(n->udx->read_buf), &(n->udx->read_buf_free));
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_stream_finalize (udx_stream_t *stream) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) stream;

  napi_delete_reference(n->env, n->on_data);
  napi_delete_reference(n->env, n->on_end);
  napi_delete_reference(n->env, n->on_drain);
  napi_delete_reference(n->env, n->on_ack);
  napi_delete_reference(n->env, n->on_send);
  napi_delete_reference(n->env, n->on_message);
  napi_delete_reference(n->env, n->on_close);
  napi_delete_reference(n->env, n->on_firewall);
  napi_delete_reference(n->env, n->on_remote_changed);
  napi_delete_reference(n->env, n->realloc_data);
  napi_delete_reference(n->env, n->realloc_message);
  napi_delete_reference(n->env, n->ctx);
}

static void
on_udx_stream_close (udx_stream_t *stream, int status) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) stream;
  if (n->udx->exiting) return;

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_close, &callback);

  napi_value argv[1];

  if (status >= 0) {
    napi_get_null(env, &(argv[0]));
  } else {
    napi_value code;
    napi_value msg;
    napi_create_string_utf8(env, uv_err_name(status), NAPI_AUTO_LENGTH, &code);
    napi_create_string_utf8(env, uv_strerror(status), NAPI_AUTO_LENGTH, &msg);
    napi_create_error(env, code, msg, &(argv[0]));
  }

  if (napi_make_callback(env, NULL, ctx, callback, 1, argv, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);
}

static int
on_udx_stream_firewall (udx_stream_t *stream, udx_socket_t *socket, const struct sockaddr *from) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) stream;
  udx_napi_socket_t *s = (udx_napi_socket_t *) socket;

  uint32_t fw = 1; // assume error means firewall it, whilst reporting the uncaught
  if (n->udx->exiting) return fw;

  int port = 0;
  char ip[INET6_ADDRSTRLEN];
  int family = 0;
  parse_address((struct sockaddr *) from, ip, INET6_ADDRSTRLEN, &port, &family);

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_firewall, &callback);

  napi_value res;
  napi_value argv[4];

  napi_get_reference_value(env, s->ctx, &(argv[0]));
  napi_create_uint32(env, port, &(argv[1]));
  napi_create_string_utf8(env, ip, NAPI_AUTO_LENGTH, &(argv[2]));
  napi_create_uint32(env, family, &(argv[3]));

  if (napi_make_callback(env, NULL, ctx, callback, 4, argv, &res) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  } else {
    napi_get_value_uint32(env, res, &fw);
  }

  napi_close_handle_scope(env, scope);

  return fw;
}

static void
on_udx_stream_remote_changed (udx_stream_t *stream) {
  udx_napi_stream_t *n = (udx_napi_stream_t *) stream;
  if (n->udx->exiting) return;

  napi_env env = n->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_remote_changed, &callback);

  if (napi_make_callback(env, NULL, ctx, callback, 0, NULL, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_lookup (udx_lookup_t *lookup, int status, const struct sockaddr *addr, int addr_len) {
  udx_napi_lookup_t *n = (udx_napi_lookup_t *) lookup;
  if (n->udx->exiting) return;

  napi_env env = n->env;

  char ip[INET6_ADDRSTRLEN] = "";
  int family = 0;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, n->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, n->on_lookup, &callback);

  if (status >= 0) {
    if (addr->sa_family == AF_INET) {
      uv_ip4_name((struct sockaddr_in *) addr, ip, addr_len);
      family = 4;
    } else if (addr->sa_family == AF_INET6) {
      uv_ip6_name((struct sockaddr_in6 *) addr, ip, addr_len);
      family = 6;
    }

    napi_value argv[3];
    napi_get_null(env, &(argv[0]));
    napi_create_string_utf8(env, ip, NAPI_AUTO_LENGTH, &(argv[1]));
    napi_create_uint32(env, family, &(argv[2]));

    if (napi_make_callback(env, NULL, ctx, callback, 3, argv, NULL) == napi_pending_exception) {
      napi_value fatal_exception;
      napi_get_and_clear_last_exception(env, &fatal_exception);
      napi_fatal_exception(env, fatal_exception);
    }
  } else {
    napi_value argv[1];
    napi_value code;
    napi_value msg;
    napi_create_string_utf8(env, uv_err_name(status), NAPI_AUTO_LENGTH, &code);
    napi_create_string_utf8(env, uv_strerror(status), NAPI_AUTO_LENGTH, &msg);
    napi_create_error(env, code, msg, &(argv[0]));

    if (napi_make_callback(env, NULL, ctx, callback, 1, argv, NULL) == napi_pending_exception) {
      napi_value fatal_exception;
      napi_get_and_clear_last_exception(env, &fatal_exception);
      napi_fatal_exception(env, fatal_exception);
    }
  }

  free(n->host);

  napi_close_handle_scope(env, scope);

  napi_delete_reference(n->env, n->on_lookup);
  napi_delete_reference(n->env, n->ctx);
}

static void
on_udx_interface_event (udx_interface_event_t *handle, int status) {
  udx_napi_interface_event_t *e = (udx_napi_interface_event_t *) handle;
  if (e->udx->exiting) return;

  napi_env env = e->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, e->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, e->on_event, &callback);

  if (napi_make_callback(env, NULL, ctx, callback, 0, NULL, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);
}

static void
on_udx_interface_event_close (udx_interface_event_t *handle) {
  udx_napi_interface_event_t *e = (udx_napi_interface_event_t *) handle;
  if (e->udx->exiting) return;

  napi_env env = e->env;

  napi_handle_scope scope;
  napi_open_handle_scope(env, &scope);

  napi_value ctx;
  napi_get_reference_value(env, e->ctx, &ctx);

  napi_value callback;
  napi_get_reference_value(env, e->on_close, &callback);

  if (napi_make_callback(env, NULL, ctx, callback, 0, NULL, NULL) == napi_pending_exception) {
    napi_value fatal_exception;
    napi_get_and_clear_last_exception(env, &fatal_exception);
    napi_fatal_exception(env, fatal_exception);
  }

  napi_close_handle_scope(env, scope);

  napi_delete_reference(env, e->on_event);
  napi_delete_reference(env, e->on_close);
  napi_delete_reference(env, e->ctx);
}

static void
on_udx_idle (udx_t *u) {
  udx_napi_t *self = (udx_napi_t *) u;
  if (!self->has_teardown) return;

  self->has_teardown = false;
  napi_remove_async_cleanup_hook(self->teardown);
}

napi_value
udx_napi_init (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_napi_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  char *read_buf;
  size_t read_buf_len;
  napi_get_buffer_info(env, argv[1], (void **) &read_buf, &read_buf_len);

  uv_loop_t *loop;
  napi_get_uv_event_loop(env, &loop);

  udx_init(loop, &(self->udx), on_udx_idle);

  self->read_buf = read_buf;
  self->read_buf_free = read_buf_len;
  self->exiting = false;
  self->has_teardown = false;

  return NULL;
}

napi_value
udx_napi_socket_init (napi_env env, napi_callback_info info) {
  napi_value argv[7];
  size_t argc = 7;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_napi_t *udx;
  size_t udx_len;
  napi_get_buffer_info(env, argv[0], (void **) &udx, &udx_len);

  udx_napi_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[1], (void **) &self, &self_len);

  udx_socket_t *socket = (udx_socket_t *) self;

  self->udx = udx;
  self->env = env;
  napi_create_reference(env, argv[2], 1, &(self->ctx));
  napi_create_reference(env, argv[3], 1, &(self->on_send));
  napi_create_reference(env, argv[4], 1, &(self->on_message));
  napi_create_reference(env, argv[5], 1, &(self->on_close));
  napi_create_reference(env, argv[6], 1, &(self->realloc_message));

  int err = udx_socket_init((udx_t *) udx, socket, on_udx_close);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  ensure_teardown(env, udx);

  return NULL;
}

napi_value
udx_napi_socket_bind (napi_env env, napi_callback_info info) {
  napi_value argv[5];
  size_t argc = 5;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  uint32_t port;
  napi_get_value_uint32(env, argv[1], &port);

  char ip[INET6_ADDRSTRLEN];
  size_t ip_len;
  napi_get_value_string_utf8(env, argv[2], (char *) &ip, INET6_ADDRSTRLEN, &ip_len);

  uint32_t family;
  napi_get_value_uint32(env, argv[3], &family);

  uint32_t flags;
  napi_get_value_uint32(env, argv[4], &flags);

  int err;

  struct sockaddr_storage addr;
  int addr_len;

  if (family == 4) {
    addr_len = sizeof(struct sockaddr_in);
    err = uv_ip4_addr(ip, port, (struct sockaddr_in *) &addr);
  } else {
    addr_len = sizeof(struct sockaddr_in6);
    err = uv_ip6_addr(ip, port, (struct sockaddr_in6 *) &addr);
  }

  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  err = udx_socket_bind(self, (struct sockaddr *) &addr, flags);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  // TODO: move the bottom stuff into another function, start, so error handling is easier

  struct sockaddr_storage name;

  // wont error in practice
  err = udx_socket_getsockname(self, (struct sockaddr *) &name, &addr_len);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  int local_port;

  if (family == 4) {
    local_port = ntohs(((struct sockaddr_in *) &name)->sin_port);
  } else {
    local_port = ntohs(((struct sockaddr_in6 *) &name)->sin6_port);
  }

  // wont error in practice
  err = udx_socket_recv_start(self, on_udx_message);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, local_port, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_socket_set_ttl (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  uint32_t ttl;
  napi_get_value_uint32(env, argv[1], &ttl);

  int err = udx_socket_set_ttl(self, ttl);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_socket_set_membership (napi_env env, napi_callback_info info) {
  napi_value argv[4];
  size_t argc = 4;

  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *socket;
  size_t socket_len;
  napi_get_buffer_info(env, argv[0], (void **) &socket, &socket_len);

  char mcast_addr[INET6_ADDRSTRLEN];
  size_t mcast_addr_len;
  napi_get_value_string_utf8(env, argv[1], mcast_addr, INET6_ADDRSTRLEN, &mcast_addr_len);

  char iface_addr[INET6_ADDRSTRLEN];
  size_t iface_addr_len;
  napi_get_value_string_utf8(env, argv[2], iface_addr, INET6_ADDRSTRLEN, &iface_addr_len);

  char *iface_param = iface_addr_len > 0 ? iface_addr : NULL;

  bool join; // true for join, false for leave
  napi_get_value_bool(env, argv[3], &join);

  int err = udx_socket_set_membership(socket, mcast_addr, iface_param, join ? UV_JOIN_GROUP : UV_LEAVE_GROUP);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_socket_get_recv_buffer_size (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  int size = 0;

  int err = udx_socket_get_recv_buffer_size(self, &size);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, size, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_socket_set_recv_buffer_size (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  int32_t size;
  napi_get_value_int32(env, argv[1], &size);

  int err = udx_socket_set_recv_buffer_size(self, size);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_socket_get_send_buffer_size (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  int size = 0;

  int err = udx_socket_get_send_buffer_size(self, &size);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, size, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_socket_set_send_buffer_size (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  int32_t size;
  napi_get_value_int32(env, argv[1], &size);

  int err = udx_socket_set_send_buffer_size(self, size);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, size, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_socket_send_ttl (napi_env env, napi_callback_info info) {
  napi_value argv[8];
  size_t argc = 8;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  udx_socket_send_t *req;
  size_t req_len;
  napi_get_buffer_info(env, argv[1], (void **) &req, &req_len);

  uint32_t rid;
  napi_get_value_uint32(env, argv[2], &rid);

  char *buf;
  size_t buf_len;
  napi_get_buffer_info(env, argv[3], (void **) &buf, &buf_len);

  uint32_t port;
  napi_get_value_uint32(env, argv[4], &port);

  char ip[INET6_ADDRSTRLEN];
  size_t ip_len;
  napi_get_value_string_utf8(env, argv[5], (char *) &ip, INET6_ADDRSTRLEN, &ip_len);

  uint32_t family;
  napi_get_value_uint32(env, argv[6], &family);

  uint32_t ttl;
  napi_get_value_uint32(env, argv[7], &ttl);

  req->data = (void *) ((uintptr_t) rid);

  int err;

  struct sockaddr_storage addr;

  if (family == 4) {
    err = uv_ip4_addr(ip, port, (struct sockaddr_in *) &addr);
  } else {
    err = uv_ip6_addr(ip, port, (struct sockaddr_in6 *) &addr);
  }

  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  uv_buf_t b = uv_buf_init(buf, buf_len);

  udx_socket_send_ttl(req, self, &b, 1, (const struct sockaddr *) &addr, ttl, on_udx_send);

  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_socket_close (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_socket_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[0], (void **) &self, &self_len);

  int err = udx_socket_close(self);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_stream_init (napi_env env, napi_callback_info info) {
  napi_value argv[16];
  size_t argc = 16;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_napi_t *udx;
  size_t udx_len;
  napi_get_buffer_info(env, argv[0], (void **) &udx, &udx_len);

  udx_napi_stream_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[1], (void **) &self, &self_len);

  uint32_t id;
  napi_get_value_uint32(env, argv[2], &id);

  uint32_t framed;
  napi_get_value_uint32(env, argv[3], &framed);

  udx_stream_t *stream = (udx_stream_t *) self;

  self->mode = framed ? UDX_NAPI_FRAMED : UDX_NAPI_INTERACTIVE;

  self->frame_len = -1;

  self->read_buf = NULL;
  self->read_buf_head = NULL;
  self->read_buf_free = 0;

  self->udx = udx;
  self->env = env;
  napi_create_reference(env, argv[4], 1, &(self->ctx));
  napi_create_reference(env, argv[5], 1, &(self->on_data));
  napi_create_reference(env, argv[6], 1, &(self->on_end));
  napi_create_reference(env, argv[7], 1, &(self->on_drain));
  napi_create_reference(env, argv[8], 1, &(self->on_ack));
  napi_create_reference(env, argv[9], 1, &(self->on_send));
  napi_create_reference(env, argv[10], 1, &(self->on_message));
  napi_create_reference(env, argv[11], 1, &(self->on_close));
  napi_create_reference(env, argv[12], 1, &(self->on_firewall));
  napi_create_reference(env, argv[13], 1, &(self->on_remote_changed));
  napi_create_reference(env, argv[14], 1, &(self->realloc_data));
  napi_create_reference(env, argv[15], 1, &(self->realloc_message));

  int err = udx_stream_init((udx_t *) udx, stream, id, on_udx_stream_close, on_udx_stream_finalize);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  udx_stream_firewall(stream, on_udx_stream_firewall);
  udx_stream_write_resume(stream, on_udx_stream_drain);

  ensure_teardown(env, udx);

  return NULL;
}

napi_value
udx_napi_stream_set_seq (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  uint32_t seq;
  napi_get_value_uint32(env, argv[1], &seq);

  int err = udx_stream_set_seq(stream, seq);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_stream_set_ack (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  uint32_t ack;
  napi_get_value_uint32(env, argv[1], &ack);

  int err = udx_stream_set_ack(stream, ack);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_stream_set_mode (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_napi_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  uint32_t mode;
  napi_get_value_uint32(env, argv[1], &mode);

  stream->mode = mode;

  return NULL;
}

napi_value
udx_napi_stream_recv_start (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_napi_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  char *read_buf;
  size_t read_buf_len;
  napi_get_buffer_info(env, argv[1], (void **) &read_buf, &read_buf_len);

  stream->read_buf = read_buf;
  stream->read_buf_head = read_buf;
  stream->read_buf_free = read_buf_len;

  udx_stream_read_start((udx_stream_t *) stream, on_udx_stream_read);
  udx_stream_recv_start((udx_stream_t *) stream, on_udx_stream_recv);

  return NULL;
}

napi_value
udx_napi_stream_connect (napi_env env, napi_callback_info info) {
  napi_value argv[6];
  size_t argc = 6;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  udx_socket_t *socket;
  size_t socket_len;
  napi_get_buffer_info(env, argv[1], (void **) &socket, &socket_len);

  uint32_t remote_id;
  napi_get_value_uint32(env, argv[2], &remote_id);

  uint32_t port;
  napi_get_value_uint32(env, argv[3], &port);

  char ip[INET6_ADDRSTRLEN];
  size_t ip_len;
  napi_get_value_string_utf8(env, argv[4], (char *) &ip, INET6_ADDRSTRLEN, &ip_len);

  uint32_t family;
  napi_get_value_uint32(env, argv[5], &family);

  int err;

  struct sockaddr_storage addr;

  if (family == 4) {
    err = uv_ip4_addr(ip, port, (struct sockaddr_in *) &addr);
  } else {
    err = uv_ip6_addr(ip, port, (struct sockaddr_in6 *) &addr);
  }

  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  err = udx_stream_connect(stream, socket, remote_id, (const struct sockaddr *) &addr);

  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_stream_change_remote (napi_env env, napi_callback_info info) {
  napi_value argv[6];
  size_t argc = 6;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  udx_socket_t *socket;
  size_t socket_len;
  napi_get_buffer_info(env, argv[1], (void **) &socket, &socket_len);

  uint32_t remote_id;
  napi_get_value_uint32(env, argv[2], &remote_id);

  uint32_t port;
  napi_get_value_uint32(env, argv[3], &port);

  char ip[INET6_ADDRSTRLEN];
  size_t ip_len;
  napi_get_value_string_utf8(env, argv[4], (char *) &ip, INET6_ADDRSTRLEN, &ip_len);

  uint32_t family;
  napi_get_value_uint32(env, argv[5], &family);

  int err;

  struct sockaddr_storage addr;

  if (family == 4) {
    err = uv_ip4_addr(ip, port, (struct sockaddr_in *) &addr);
  } else {
    err = uv_ip6_addr(ip, port, (struct sockaddr_in6 *) &addr);
  }

  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  err = udx_stream_change_remote(stream, socket, remote_id, (const struct sockaddr *) &addr, on_udx_stream_remote_changed);

  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_stream_relay_to (napi_env env, napi_callback_info info) {
  napi_value argv[2];
  size_t argc = 2;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  udx_stream_t *destination;
  size_t destination_len;
  napi_get_buffer_info(env, argv[1], (void **) &destination, &destination_len);

  int err = udx_stream_relay_to(stream, destination);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_stream_send (napi_env env, napi_callback_info info) {
  napi_value argv[4];
  size_t argc = 4;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  udx_stream_send_t *req;
  size_t req_len;
  napi_get_buffer_info(env, argv[1], (void **) &req, &req_len);

  uint32_t rid;
  napi_get_value_uint32(env, argv[2], &rid);

  char *buf;
  size_t buf_len;
  napi_get_buffer_info(env, argv[3], (void **) &buf, &buf_len);

  req->data = (void *) ((uintptr_t) rid);

  uv_buf_t b = uv_buf_init(buf, buf_len);

  int err = udx_stream_send(req, stream, &b, 1, on_udx_stream_send);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, err, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_stream_write (napi_env env, napi_callback_info info) {
  napi_value argv[4];
  size_t argc = 4;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  udx_stream_write_t *req;
  size_t req_len;
  napi_get_buffer_info(env, argv[1], (void **) &req, &req_len);

  uint32_t rid;
  napi_get_value_uint32(env, argv[2], &rid);

  char *buf;
  size_t buf_len;
  napi_get_buffer_info(env, argv[3], (void **) &buf, &buf_len);

  req->data = (void *) ((uintptr_t) rid);

  uv_buf_t b = uv_buf_init(buf, buf_len);

  int err = udx_stream_write(req, stream, &b, 1, on_udx_stream_ack);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, err, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_stream_writev (napi_env env, napi_callback_info info) {
  napi_value argv[4];
  size_t argc = 4;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  udx_stream_write_t *req;
  size_t req_len;
  napi_get_buffer_info(env, argv[1], (void **) &req, &req_len);

  uint32_t rid;
  napi_get_value_uint32(env, argv[2], &rid);

  napi_value buffers = argv[3];

  req->data = (void *) ((uintptr_t) rid);

  uint32_t len;
  napi_get_array_length(env, buffers, &len);
  uv_buf_t *batch = malloc(sizeof(uv_buf_t) * len);

  napi_value element;
  for (uint32_t i = 0; i < len; i++) {
    napi_get_element(env, buffers, i, &element);

    char *buf;
    size_t buf_len;
    napi_get_buffer_info(env, element, (void **) &buf, &buf_len);

    batch[i] = uv_buf_init(buf, buf_len);
  }

  int err = udx_stream_write(req, stream, batch, len, on_udx_stream_ack);
  free(batch);

  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, err, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_stream_write_sizeof (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  uint32_t bufs;
  napi_get_value_uint32(env, argv[0], &bufs);

  napi_value return_uint32;
  napi_create_uint32(env, udx_stream_write_sizeof(bufs), &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_stream_write_end (napi_env env, napi_callback_info info) {
  napi_value argv[4];
  size_t argc = 4;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  udx_stream_write_t *req;
  size_t req_len;
  napi_get_buffer_info(env, argv[1], (void **) &req, &req_len);

  uint32_t rid;
  napi_get_value_uint32(env, argv[2], &rid);

  char *buf;
  size_t buf_len;
  napi_get_buffer_info(env, argv[3], (void **) &buf, &buf_len);

  req->data = (void *) ((uintptr_t) rid);

  uv_buf_t b = uv_buf_init(buf, buf_len);

  int err = udx_stream_write_end(req, stream, &b, 1, on_udx_stream_ack);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, err, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_stream_destroy (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_stream_t *stream;
  size_t stream_len;
  napi_get_buffer_info(env, argv[0], (void **) &stream, &stream_len);

  int err = udx_stream_destroy(stream);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  napi_value return_uint32;
  napi_create_uint32(env, err, &return_uint32);

  return return_uint32;
}

napi_value
udx_napi_lookup (napi_env env, napi_callback_info info) {
  napi_value argv[6];
  size_t argc = 6;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_napi_t *udx;
  size_t udx_len;
  napi_get_buffer_info(env, argv[0], (void **) &udx, &udx_len);

  udx_napi_lookup_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[1], (void **) &self, &self_len);

  self->udx = udx;

  size_t host_size = 0;
  napi_get_value_string_utf8(env, argv[2], NULL, 0, &host_size);

  char *host = (char *) malloc((host_size + 1) * sizeof(char));
  size_t host_len;
  napi_get_value_string_utf8(env, argv[2], host, host_size + 1, &host_len);
  host[host_size] = '\0';

  uint32_t family;
  napi_get_value_uint32(env, argv[3], &family);

  udx_lookup_t *lookup = (udx_lookup_t *) self;

  self->host = host;
  self->env = env;
  napi_create_reference(env, argv[4], 1, &(self->ctx));
  napi_create_reference(env, argv[5], 1, &(self->on_lookup));

  int flags = 0;

  if (family == 4) flags |= UDX_LOOKUP_FAMILY_IPV4;
  if (family == 6) flags |= UDX_LOOKUP_FAMILY_IPV6;

  int err = udx_lookup((udx_t *) udx, lookup, host, flags, on_udx_lookup);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  ensure_teardown(env, udx);

  return NULL;
}

napi_value
udx_napi_interface_event_init (napi_env env, napi_callback_info info) {
  napi_value argv[5];
  size_t argc = 5;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_napi_t *udx;
  size_t udx_len;
  napi_get_buffer_info(env, argv[0], (void **) &udx, &udx_len);

  udx_napi_interface_event_t *self;
  size_t self_len;
  napi_get_buffer_info(env, argv[1], (void **) &self, &self_len);

  self->udx = udx;

  udx_interface_event_t *event = (udx_interface_event_t *) self;

  self->env = env;
  napi_create_reference(env, argv[2], 1, &(self->ctx));
  napi_create_reference(env, argv[3], 1, &(self->on_event));
  napi_create_reference(env, argv[4], 1, &(self->on_close));

  int err = udx_interface_event_init((udx_t *) udx, event, on_udx_interface_event_close);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  err = udx_interface_event_start(event, on_udx_interface_event, 5000);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  ensure_teardown(env, udx);

  return NULL;
}

napi_value
udx_napi_interface_event_start (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_interface_event_t *event;
  size_t event_len;
  napi_get_buffer_info(env, argv[0], (void **) &event, &event_len);

  int err = udx_interface_event_start(event, on_udx_interface_event, 5000);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_interface_event_stop (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_interface_event_t *event;
  size_t event_len;
  napi_get_buffer_info(env, argv[0], (void **) &event, &event_len);

  int err = udx_interface_event_stop(event);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_interface_event_close (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_interface_event_t *event;
  size_t event_len;
  napi_get_buffer_info(env, argv[0], (void **) &event, &event_len);

  int err = udx_interface_event_close(event);
  if (err < 0) {
    napi_throw_error(env, uv_err_name(err), uv_strerror(err));
    return NULL;
  }

  return NULL;
}

napi_value
udx_napi_interface_event_get_addrs (napi_env env, napi_callback_info info) {
  napi_value argv[1];
  size_t argc = 1;
  napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

  udx_interface_event_t *event;
  size_t event_len;
  napi_get_buffer_info(env, argv[0], (void **) &event, &event_len);

  char ip[INET6_ADDRSTRLEN];
  int family = 0;

  napi_value napi_result;
  napi_create_array(env, &napi_result);

  for (int i = 0, j = 0; i < event->addrs_len; i++) {
    uv_interface_address_t addr = event->addrs[i];

    if (addr.address.address4.sin_family == AF_INET) {
      uv_ip4_name(&addr.address.address4, ip, sizeof(ip));
      family = 4;
    } else if (addr.address.address4.sin_family == AF_INET6) {
      uv_ip6_name(&addr.address.address6, ip, sizeof(ip));
      family = 6;
    } else {
      continue;
    }

    napi_value napi_item;
    napi_create_object(env, &napi_item);
    napi_set_element(env, napi_result, j++, napi_item);

    napi_value napi_name;
    napi_create_string_utf8(env, addr.name, NAPI_AUTO_LENGTH, &napi_name);
    napi_set_named_property(env, napi_item, "name", napi_name);

    napi_value napi_ip;
    napi_create_string_utf8(env, ip, NAPI_AUTO_LENGTH, &napi_ip);
    napi_set_named_property(env, napi_item, "host", napi_ip);

    napi_value napi_family;
    napi_create_uint32(env, family, &napi_family);
    napi_set_named_property(env, napi_item, "family", napi_family);

    napi_value napi_internal;
    napi_get_boolean(env, addr.is_internal, &napi_internal);
    napi_set_named_property(env, napi_item, "internal", napi_internal);
  }

  return napi_result;
}

static void
napi_macros_init (napi_env env, napi_value exports);

static napi_value
napi_macros_init_wrap (napi_env env, napi_value exports) {
  napi_macros_init(env, exports);
  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, napi_macros_init_wrap)
static void
napi_macros_init (napi_env env, napi_value exports) {

  napi_value UV_UDP_IPV6ONLY_uint32;
  napi_create_uint32(env, UV_UDP_IPV6ONLY, &UV_UDP_IPV6ONLY_uint32);
  napi_set_named_property(env, exports, "UV_UDP_IPV6ONLY", UV_UDP_IPV6ONLY_uint32);

  napi_value UV_UDP_REUSEADDR_uint32;
  napi_create_uint32(env, UV_UDP_REUSEADDR, &UV_UDP_REUSEADDR_uint32);
  napi_set_named_property(env, exports, "UV_UDP_REUSEADDR", UV_UDP_REUSEADDR_uint32);

  napi_value inflight_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, inflight), &inflight_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_inflight", inflight_offsetof);

  napi_value mtu_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, mtu), &mtu_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_mtu", mtu_offsetof);

  napi_value cwnd_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, cwnd), &cwnd_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_cwnd", cwnd_offsetof);

  napi_value srtt_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, srtt), &srtt_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_srtt", srtt_offsetof);

  napi_value bytes_rx_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, bytes_rx), &bytes_rx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_bytes_rx", bytes_rx_offsetof);

  napi_value packets_rx_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, packets_rx), &packets_rx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_packets_rx", packets_rx_offsetof);

  napi_value bytes_tx_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, bytes_tx), &bytes_tx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_bytes_tx", bytes_tx_offsetof);

  napi_value packets_tx_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, packets_tx), &packets_tx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_packets_tx", packets_tx_offsetof);

  napi_value rto_count_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, rto_count), &rto_count_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_rto_count", rto_count_offsetof);

  napi_value retransmit_count_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, retransmit_count), &retransmit_count_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_retransmit_count", retransmit_count_offsetof);

  napi_value fast_recovery_count_offsetof;
  napi_create_uint32(env, offsetof(udx_stream_t, fast_recovery_count), &fast_recovery_count_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_stream_t_fast_recovery_count", fast_recovery_count_offsetof);

  napi_value socket_bytes_rx_offsetof;
  napi_create_uint32(env, offsetof(udx_socket_t, bytes_rx), &socket_bytes_rx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_socket_t_bytes_rx", socket_bytes_rx_offsetof);

  napi_value socket_packets_rx_offsetof;
  napi_create_uint32(env, offsetof(udx_socket_t, packets_rx), &socket_packets_rx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_socket_t_packets_rx", socket_packets_rx_offsetof);

  napi_value socket_bytes_tx_offsetof;
  napi_create_uint32(env, offsetof(udx_socket_t, bytes_tx), &socket_bytes_tx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_socket_t_bytes_tx", socket_bytes_tx_offsetof);

  napi_value socket_packets_tx_offsetof;
  napi_create_uint32(env, offsetof(udx_socket_t, packets_tx), &socket_packets_tx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_socket_t_packets_tx", socket_packets_tx_offsetof);

  napi_value packets_dropped_by_kernel_offsetof;
  napi_create_uint32(env, offsetof(udx_socket_t, packets_dropped_by_kernel), &packets_dropped_by_kernel_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_socket_t_packets_dropped_by_kernel", packets_dropped_by_kernel_offsetof);

  napi_value udx_bytes_rx_offsetof;
  napi_create_uint32(env, offsetof(udx_t, bytes_rx), &udx_bytes_rx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_t_bytes_rx", udx_bytes_rx_offsetof);

  napi_value udx_packets_rx_offsetof;
  napi_create_uint32(env, offsetof(udx_t, packets_rx), &udx_packets_rx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_t_packets_rx", udx_packets_rx_offsetof);

  napi_value udx_bytes_tx_offsetof;
  napi_create_uint32(env, offsetof(udx_t, bytes_tx), &udx_bytes_tx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_t_bytes_tx", udx_bytes_tx_offsetof);

  napi_value udx_packets_tx_offsetof;
  napi_create_uint32(env, offsetof(udx_t, packets_tx), &udx_packets_tx_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_t_packets_tx", udx_packets_tx_offsetof);

  napi_value udx_packets_dropped_by_kernel_offsetof;
  napi_create_uint32(env, offsetof(udx_t, packets_dropped_by_kernel), &udx_packets_dropped_by_kernel_offsetof);
  napi_set_named_property(env, exports, "offsetof_udx_t_packets_dropped_by_kernel", udx_packets_dropped_by_kernel_offsetof);

  napi_value udx_napi_t_sizeof;
  napi_create_uint32(env, sizeof(udx_napi_t), &udx_napi_t_sizeof);
  napi_set_named_property(env, exports, "sizeof_udx_napi_t", udx_napi_t_sizeof);

  napi_value udx_napi_socket_t_sizeof;
  napi_create_uint32(env, sizeof(udx_napi_socket_t), &udx_napi_socket_t_sizeof);
  napi_set_named_property(env, exports, "sizeof_udx_napi_socket_t", udx_napi_socket_t_sizeof);

  napi_value udx_napi_stream_t_sizeof;
  napi_create_uint32(env, sizeof(udx_napi_stream_t), &udx_napi_stream_t_sizeof);
  napi_set_named_property(env, exports, "sizeof_udx_napi_stream_t", udx_napi_stream_t_sizeof);

  napi_value udx_napi_lookup_t_sizeof;
  napi_create_uint32(env, sizeof(udx_napi_lookup_t), &udx_napi_lookup_t_sizeof);
  napi_set_named_property(env, exports, "sizeof_udx_napi_lookup_t", udx_napi_lookup_t_sizeof);

  napi_value udx_napi_interface_event_t_sizeof;
  napi_create_uint32(env, sizeof(udx_napi_interface_event_t), &udx_napi_interface_event_t_sizeof);
  napi_set_named_property(env, exports, "sizeof_udx_napi_interface_event_t", udx_napi_interface_event_t_sizeof);

  napi_value udx_socket_send_t_sizeof;
  napi_create_uint32(env, sizeof(udx_socket_send_t), &udx_socket_send_t_sizeof);
  napi_set_named_property(env, exports, "sizeof_udx_socket_send_t", udx_socket_send_t_sizeof);

  napi_value udx_stream_send_t_sizeof;
  napi_create_uint32(env, sizeof(udx_stream_send_t), &udx_stream_send_t_sizeof);
  napi_set_named_property(env, exports, "sizeof_udx_stream_send_t", udx_stream_send_t_sizeof);

  napi_value udx_napi_init_fn;
  napi_create_function(env, NULL, 0, udx_napi_init, NULL, &udx_napi_init_fn);
  napi_set_named_property(env, exports, "udx_napi_init", udx_napi_init_fn);

  napi_value udx_napi_socket_init_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_init, NULL, &udx_napi_socket_init_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_init", udx_napi_socket_init_fn);

  napi_value udx_napi_socket_bind_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_bind, NULL, &udx_napi_socket_bind_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_bind", udx_napi_socket_bind_fn);

  napi_value udx_napi_socket_set_ttl_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_set_ttl, NULL, &udx_napi_socket_set_ttl_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_set_ttl", udx_napi_socket_set_ttl_fn);

  napi_value udx_napi_socket_get_recv_buffer_size_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_get_recv_buffer_size, NULL, &udx_napi_socket_get_recv_buffer_size_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_get_recv_buffer_size", udx_napi_socket_get_recv_buffer_size_fn);

  napi_value udx_napi_socket_set_recv_buffer_size_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_set_recv_buffer_size, NULL, &udx_napi_socket_set_recv_buffer_size_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_set_recv_buffer_size", udx_napi_socket_set_recv_buffer_size_fn);

  napi_value udx_napi_socket_get_send_buffer_size_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_get_send_buffer_size, NULL, &udx_napi_socket_get_send_buffer_size_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_get_send_buffer_size", udx_napi_socket_get_send_buffer_size_fn);

  napi_value udx_napi_socket_set_send_buffer_size_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_set_send_buffer_size, NULL, &udx_napi_socket_set_send_buffer_size_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_set_send_buffer_size", udx_napi_socket_set_send_buffer_size_fn);

  napi_value udx_napi_socket_set_membership_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_set_membership, NULL, &udx_napi_socket_set_membership_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_set_membership", udx_napi_socket_set_membership_fn);

  napi_value udx_napi_socket_send_ttl_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_send_ttl, NULL, &udx_napi_socket_send_ttl_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_send_ttl", udx_napi_socket_send_ttl_fn);

  napi_value udx_napi_socket_close_fn;
  napi_create_function(env, NULL, 0, udx_napi_socket_close, NULL, &udx_napi_socket_close_fn);
  napi_set_named_property(env, exports, "udx_napi_socket_close", udx_napi_socket_close_fn);

  napi_value udx_napi_stream_init_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_init, NULL, &udx_napi_stream_init_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_init", udx_napi_stream_init_fn);

  napi_value udx_napi_stream_set_seq_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_set_seq, NULL, &udx_napi_stream_set_seq_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_set_seq", udx_napi_stream_set_seq_fn);

  napi_value udx_napi_stream_set_ack_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_set_ack, NULL, &udx_napi_stream_set_ack_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_set_ack", udx_napi_stream_set_ack_fn);

  napi_value udx_napi_stream_set_mode_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_set_mode, NULL, &udx_napi_stream_set_mode_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_set_mode", udx_napi_stream_set_mode_fn);

  napi_value udx_napi_stream_connect_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_connect, NULL, &udx_napi_stream_connect_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_connect", udx_napi_stream_connect_fn);

  napi_value udx_napi_stream_change_remote_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_change_remote, NULL, &udx_napi_stream_change_remote_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_change_remote", udx_napi_stream_change_remote_fn);

  napi_value udx_napi_stream_relay_to_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_relay_to, NULL, &udx_napi_stream_relay_to_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_relay_to", udx_napi_stream_relay_to_fn);

  napi_value udx_napi_stream_send_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_send, NULL, &udx_napi_stream_send_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_send", udx_napi_stream_send_fn);

  napi_value udx_napi_stream_recv_start_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_recv_start, NULL, &udx_napi_stream_recv_start_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_recv_start", udx_napi_stream_recv_start_fn);

  napi_value udx_napi_stream_write_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_write, NULL, &udx_napi_stream_write_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_write", udx_napi_stream_write_fn);

  napi_value udx_napi_stream_writev_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_writev, NULL, &udx_napi_stream_writev_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_writev", udx_napi_stream_writev_fn);

  napi_value udx_napi_stream_write_sizeof_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_write_sizeof, NULL, &udx_napi_stream_write_sizeof_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_write_sizeof", udx_napi_stream_write_sizeof_fn);

  napi_value udx_napi_stream_write_end_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_write_end, NULL, &udx_napi_stream_write_end_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_write_end", udx_napi_stream_write_end_fn);

  napi_value udx_napi_stream_destroy_fn;
  napi_create_function(env, NULL, 0, udx_napi_stream_destroy, NULL, &udx_napi_stream_destroy_fn);
  napi_set_named_property(env, exports, "udx_napi_stream_destroy", udx_napi_stream_destroy_fn);

  napi_value udx_napi_lookup_fn;
  napi_create_function(env, NULL, 0, udx_napi_lookup, NULL, &udx_napi_lookup_fn);
  napi_set_named_property(env, exports, "udx_napi_lookup", udx_napi_lookup_fn);

  napi_value udx_napi_interface_event_init_fn;
  napi_create_function(env, NULL, 0, udx_napi_interface_event_init, NULL, &udx_napi_interface_event_init_fn);
  napi_set_named_property(env, exports, "udx_napi_interface_event_init", udx_napi_interface_event_init_fn);

  napi_value udx_napi_interface_event_start_fn;
  napi_create_function(env, NULL, 0, udx_napi_interface_event_start, NULL, &udx_napi_interface_event_start_fn);
  napi_set_named_property(env, exports, "udx_napi_interface_event_start", udx_napi_interface_event_start_fn);

  napi_value udx_napi_interface_event_stop_fn;
  napi_create_function(env, NULL, 0, udx_napi_interface_event_stop, NULL, &udx_napi_interface_event_stop_fn);
  napi_set_named_property(env, exports, "udx_napi_interface_event_stop", udx_napi_interface_event_stop_fn);

  napi_value udx_napi_interface_event_close_fn;
  napi_create_function(env, NULL, 0, udx_napi_interface_event_close, NULL, &udx_napi_interface_event_close_fn);
  napi_set_named_property(env, exports, "udx_napi_interface_event_close", udx_napi_interface_event_close_fn);

  napi_value udx_napi_interface_event_get_addrs_fn;
  napi_create_function(env, NULL, 0, udx_napi_interface_event_get_addrs, NULL, &udx_napi_interface_event_get_addrs_fn);
  napi_set_named_property(env, exports, "udx_napi_interface_event_get_addrs", udx_napi_interface_event_get_addrs_fn);
}