/*!
 * Low-level bindings to the libuv library.
 *
 * This module contains a set of direct, 'bare-metal' wrappers around
 * the libuv C-API.
 *
 * Also contained herein are a set of rust records that map, in
 * approximate memory-size, to the libuv data structures. The record
 * implementations are adjusted, per-platform, to match their respective
 * representations.
 *
 * There are also a collection of helper functions to ease interacting
 * with the low-level API (such as a function to return the latest
 * libuv error as a rust-formatted string).
 *
 * As new functionality, existant in uv.h, is added to the rust stdlib,
 * the mappings should be added in this module.
 *
 * This module's implementation will hopefully be, eventually, replaced
 * with per-platform, generated source files from rust-bindgen.
 */

import libc::size_t;

// libuv struct mappings
type uv_ip4_addr = {
    ip: ~[u8],
    port: int
};
type uv_ip6_addr = uv_ip4_addr;

enum uv_handle_type {
    UNKNOWN_HANDLE = 0,
    UV_TCP,
    UV_UDP,
    UV_NAMED_PIPE,
    UV_TTY,
    UV_FILE,
    UV_TIMER,
    UV_PREPARE,
    UV_CHECK,
    UV_IDLE,
    UV_ASYNC,
    UV_ARES_TASK,
    UV_ARES_EVENT,
    UV_PROCESS,
    UV_FS_EVENT
}

type handle_type = libc::c_uint;

type uv_handle_fields = {
   loop_handle: *libc::c_void,
   type_: handle_type,
   close_cb: *u8,
   mut data: *libc::c_void,
};

// unix size: 8
type uv_err_t = {
    code: libc::c_int,
    sys_errno_: libc::c_int
};

// don't create one of these directly. instead,
// count on it appearing in libuv callbacks or embedded
// in other types as a pointer to be used in other
// operations (so mostly treat it as opaque, once you
// have it in this form..)
type uv_stream_t = {
    fields: uv_handle_fields
};

// 64bit unix size: 272
#[cfg(unix)]
type uv_tcp_t = {
    fields: uv_handle_fields,
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8, a10: *u8, a11: *u8,
    a12: *u8, a13: *u8, a14: *u8, a15: *u8,
    a16: *u8, a17: *u8, a18: *u8, a19: *u8,
    a20: *u8, a21: *u8, a22: *u8, a23: *u8,
    a24: *u8, a25: *u8, a26: *u8, a27: *u8,
    a28: *u8,
    a30: uv_tcp_t_32bit_unix_riders
};
// 32bit unix size: 328 (164)
#[cfg(target_arch="x86_64")]
type uv_tcp_t_32bit_unix_riders = {
    a29: *u8
};
#[cfg(target_arch="x86")]
type uv_tcp_t_32bit_unix_riders = {
    a29: *u8, a30: *u8, a31: *u8,
    a32: *u8, a33: *u8, a34: *u8,
    a35: *u8, a36: *u8
};

// 32bit win32 size: 240 (120)
#[cfg(windows)]
type uv_tcp_t = {
    fields: uv_handle_fields,
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8, a10: *u8, a11: *u8,
    a12: *u8, a13: *u8, a14: *u8, a15: *u8,
    a16: *u8, a17: *u8, a18: *u8, a19: *u8,
    a20: *u8, a21: *u8, a22: *u8, a23: *u8,
    a24: *u8, a25: *u8
};

// unix size: 48
#[cfg(unix)]
type uv_connect_t = {
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8
};
// win32 size: 88 (44)
#[cfg(windows)]
type uv_connect_t = {
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8, a10: *u8
};

// unix size: 16
type uv_buf_t = {
    base: *u8,
    len: libc::size_t
};
// no gen stub method.. should create
// it via uv::direct::buf_init()

// unix size: 144
#[cfg(unix)]
type uv_write_t = {
    fields: uv_handle_fields,
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8, a10: *u8, a11: *u8,
    a12: *u8,
    a14: uv_write_t_32bit_unix_riders
};
#[cfg(target_arch="x86_64")]
type uv_write_t_32bit_unix_riders = {
    a13: *u8
};
#[cfg(target_arch="x86")]
type uv_write_t_32bit_unix_riders = {
    a13: *u8, a14: *u8
};
// win32 size: 136 (68)
#[cfg(windows)]
type uv_write_t = {
    fields: uv_handle_fields,
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8, a10: *u8, a11: *u8,
    a12: *u8
};
// 64bit unix size: 120
// 32bit unix size: 152 (76)
#[cfg(unix)]
type uv_async_t = {
    fields: uv_handle_fields,
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8,
    a11: uv_async_t_32bit_unix_riders
};
#[cfg(target_arch="x86_64")]
type uv_async_t_32bit_unix_riders = {
    a10: *u8
};
#[cfg(target_arch="x86")]
type uv_async_t_32bit_unix_riders = {
    a10: *u8, a11: *u8, a12: *u8, a13: *u8
};
// win32 size 132 (68)
#[cfg(windows)]
type uv_async_t = {
    fields: uv_handle_fields,
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8, a10: *u8, a11: *u8,
    a12: *u8
};

// 64bit unix size: 128
// 32bit unix size: 84
#[cfg(unix)]
type uv_timer_t = {
    fields: uv_handle_fields,
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8,
    a11: uv_timer_t_32bit_unix_riders
};
#[cfg(target_arch="x86_64")]
type uv_timer_t_32bit_unix_riders = {
    a10: *u8, a11: *u8
};
#[cfg(target_arch="x86")]
type uv_timer_t_32bit_unix_riders = {
    a10: *u8, a11: *u8, a12: *u8, a13: *u8,
    a14: *u8, a15: *u8, a16: *u8
};
// win32 size: 64
#[cfg(windows)]
type uv_timer_t = {
    fields: uv_handle_fields,
    a00: *u8, a01: *u8, a02: *u8, a03: *u8,
    a04: *u8, a05: *u8, a06: *u8, a07: *u8,
    a08: *u8, a09: *u8, a10: *u8, a11: *u8
};

// unix size: 16
type sockaddr_in = {
    mut sin_family: u16,
    mut sin_port: u16,
    mut sin_addr: u32, // in_addr: this is an opaque, per-platform struct
    mut sin_zero: (u8, u8, u8, u8, u8, u8, u8, u8)
};

// unix size: 28 .. FIXME #1645
// stuck with 32 becuse of rust padding structs?
#[cfg(target_arch="x86_64")]
type sockaddr_in6 = {
    a0: *u8, a1: *u8,
    a2: *u8, a3: *u8
};
#[cfg(target_arch="x86")]
type sockaddr_in6 = {
    a0: *u8, a1: *u8,
    a2: *u8, a3: *u8,
    a4: *u8, a5: *u8,
    a6: *u8, a7: *u8
};

// unix size: 28 .. FIXME #1645
// stuck with 32 becuse of rust padding structs?
type addr_in = addr_in_impl::addr_in;
#[cfg(unix)]
mod addr_in_impl {
    #[cfg(target_arch="x86_64")]
    type addr_in = {
        a0: *u8, a1: *u8,
        a2: *u8, a3: *u8
    };
    #[cfg(target_arch="x86")]
    type addr_in = {
        a0: *u8, a1: *u8,
        a2: *u8, a3: *u8,
        a4: *u8, a5: *u8,
        a6: *u8, a7: *u8,
    };
}
#[cfg(windows)]
mod addr_in_impl {
    type addr_in = {
        a0: *u8, a1: *u8,
        a2: *u8, a3: *u8
    };
}

// unix size: 48, 32bit: 32
type addrinfo = addrinfo_impl::addrinfo;
#[cfg(target_os="linux")]
mod addrinfo_impl {
    #[cfg(target_arch="x86_64")]
    type addrinfo = {
        a00: *u8, a01: *u8, a02: *u8, a03: *u8,
        a04: *u8, a05: *u8
    };
    #[cfg(target_arch="x86")]
    type addrinfo = {
        a00: *u8, a01: *u8, a02: *u8, a03: *u8,
        a04: *u8, a05: *u8, a06: *u8, a07: *u8
    };
}
#[cfg(target_os="macos")]
#[cfg(target_os="freebsd")]
mod addrinfo_impl {
    type addrinfo = {
        a00: *u8, a01: *u8, a02: *u8, a03: *u8,
        a04: *u8, a05: *u8
    };
}
#[cfg(windows)]
mod addrinfo_impl {
    type addrinfo = {
        a00: *u8, a01: *u8, a02: *u8, a03: *u8,
        a04: *u8, a05: *u8
    };
}

// unix size: 72
type uv_getaddrinfo_t = {
    a00: *u8, a01: *u8, a02: *u8, a03: *u8, a04: *u8, a05: *u8,
    a06: *u8, a07: *u8, a08: *u8
};

mod uv_ll_struct_stubgen {
    fn gen_stub_uv_tcp_t() -> uv_tcp_t {
        ret gen_stub_os();
        #[cfg(target_os = "linux")]
        #[cfg(target_os = "macos")]
        #[cfg(target_os = "freebsd")]
        fn gen_stub_os() -> uv_tcp_t {
            ret gen_stub_arch();
            #[cfg(target_arch="x86_64")]
            fn gen_stub_arch() -> uv_tcp_t {
                ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                                close_cb: ptr::null(),
                                mut data: ptr::null() },
                    a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                    a03: 0 as *u8,
                    a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                    a07: 0 as *u8,
                    a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8,
                    a11: 0 as *u8,
                    a12: 0 as *u8, a13: 0 as *u8, a14: 0 as *u8,
                    a15: 0 as *u8,
                    a16: 0 as *u8, a17: 0 as *u8, a18: 0 as *u8,
                    a19: 0 as *u8,
                    a20: 0 as *u8, a21: 0 as *u8, a22: 0 as *u8,
                    a23: 0 as *u8,
                    a24: 0 as *u8, a25: 0 as *u8, a26: 0 as *u8,
                    a27: 0 as *u8,
                    a28: 0 as *u8,
                    a30: {
                        a29: 0 as *u8
                    }
                };
            }
            #[cfg(target_arch="x86")]
            fn gen_stub_arch() -> uv_tcp_t {
                ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                                close_cb: ptr::null(),
                                mut data: ptr::null() },
                    a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                    a03: 0 as *u8,
                    a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                    a07: 0 as *u8,
                    a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8,
                    a11: 0 as *u8,
                    a12: 0 as *u8, a13: 0 as *u8, a14: 0 as *u8,
                    a15: 0 as *u8,
                    a16: 0 as *u8, a17: 0 as *u8, a18: 0 as *u8,
                    a19: 0 as *u8,
                    a20: 0 as *u8, a21: 0 as *u8, a22: 0 as *u8,
                    a23: 0 as *u8,
                    a24: 0 as *u8, a25: 0 as *u8, a26: 0 as *u8,
                    a27: 0 as *u8,
                    a28: 0 as *u8,
                    a30: {
                        a29: 0 as *u8, a30: 0 as *u8, a31: 0 as *u8,
                        a32: 0 as *u8, a33: 0 as *u8, a34: 0 as *u8,
                        a35: 0 as *u8, a36: 0 as *u8
                    }
                };
            }
        }
        #[cfg(windows)]
        fn gen_stub_os() -> uv_tcp_t {
            ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                            close_cb: ptr::null(),
                            mut data: ptr::null() },
                a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                a03: 0 as *u8,
                a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                a07: 0 as *u8,
                a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8,
                a11: 0 as *u8,
                a12: 0 as *u8, a13: 0 as *u8, a14: 0 as *u8,
                a15: 0 as *u8,
                a16: 0 as *u8, a17: 0 as *u8, a18: 0 as *u8,
                a19: 0 as *u8,
                a20: 0 as *u8, a21: 0 as *u8, a22: 0 as *u8,
                a23: 0 as *u8,
                a24: 0 as *u8, a25: 0 as *u8
            };
        }
    }
    #[cfg(unix)]
    fn gen_stub_uv_connect_t() -> uv_connect_t {
        ret {
            a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
            a03: 0 as *u8,
            a04: 0 as *u8, a05: 0 as *u8
        };
    }
    #[cfg(windows)]
    fn gen_stub_uv_connect_t() -> uv_connect_t {
        ret {
            a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
            a03: 0 as *u8,
            a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
            a07: 0 as *u8,
            a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8
        };
    }
    #[cfg(unix)]
    fn gen_stub_uv_async_t() -> uv_async_t {
        ret gen_stub_arch();
        #[cfg(target_arch = "x86_64")]
        fn gen_stub_arch() -> uv_async_t {
            ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                            close_cb: ptr::null(),
                            mut data: ptr::null() },
                a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                a03: 0 as *u8,
                a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                a07: 0 as *u8,
                a08: 0 as *u8, a09: 0 as *u8,
                a11: {
                    a10: 0 as *u8
                }
            };
        }
        #[cfg(target_arch = "x86")]
        fn gen_stub_arch() -> uv_async_t {
            ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                            close_cb: ptr::null(),
                            mut data: ptr::null() },
                a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                a03: 0 as *u8,
                a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                a07: 0 as *u8,
                a08: 0 as *u8, a09: 0 as *u8,
                a11: {
                    a10: 0 as *u8, a11: 0 as *u8,
                    a12: 0 as *u8, a13: 0 as *u8
                }
            };
        }
    }
    #[cfg(windows)]
    fn gen_stub_uv_async_t() -> uv_async_t {
        ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                        close_cb: ptr::null(),
                        mut data: ptr::null() },
            a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
            a03: 0 as *u8,
            a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
            a07: 0 as *u8,
            a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8,
            a11: 0 as *u8,
            a12: 0 as *u8
        };
    }
    #[cfg(unix)]
    fn gen_stub_uv_timer_t() -> uv_timer_t {
        ret gen_stub_arch();
        #[cfg(target_arch = "x86_64")]
        fn gen_stub_arch() -> uv_timer_t {
            ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                            close_cb: ptr::null(),
                            mut data: ptr::null() },
                a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                a03: 0 as *u8,
                a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                a07: 0 as *u8,
                a08: 0 as *u8, a09: 0 as *u8,
                a11: {
                    a10: 0 as *u8, a11: 0 as *u8
                }
            };
        }
        #[cfg(target_arch = "x86")]
        fn gen_stub_arch() -> uv_timer_t {
            ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                            close_cb: ptr::null(),
                            mut data: ptr::null() },
                a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                a03: 0 as *u8,
                a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                a07: 0 as *u8,
                a08: 0 as *u8, a09: 0 as *u8,
                a11: {
                    a10: 0 as *u8, a11: 0 as *u8,
                    a12: 0 as *u8, a13: 0 as *u8,
                    a14: 0 as *u8, a15: 0 as *u8,
                    a16: 0 as *u8
                }
            };
        }
    }
    #[cfg(windows)]
    fn gen_stub_uv_timer_t() -> uv_timer_t {
        ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                        close_cb: ptr::null(),
                        mut data: ptr::null() },
            a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
            a03: 0 as *u8,
            a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
            a07: 0 as *u8,
            a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8,
            a11: 0 as *u8
        };
    }
    #[cfg(unix)]
    fn gen_stub_uv_write_t() -> uv_write_t {
        ret gen_stub_arch();
        #[cfg(target_arch="x86_64")]
        fn gen_stub_arch() -> uv_write_t {
            ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                            close_cb: ptr::null(),
                            mut data: ptr::null() },
                a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                a03: 0 as *u8,
                a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                a07: 0 as *u8,
                a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8,
                a11: 0 as *u8,
                a12: 0 as *u8, a14: { a13: 0 as *u8 }
            };
        }
        #[cfg(target_arch="x86")]
        fn gen_stub_arch() -> uv_write_t {
            ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                            close_cb: ptr::null(),
                            mut data: ptr::null() },
                a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
                a03: 0 as *u8,
                a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
                a07: 0 as *u8,
                a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8,
                a11: 0 as *u8,
                a12: 0 as *u8, a14: { a13: 0 as *u8, a14: 0 as *u8 }
            };
        }
    }
    #[cfg(windows)]
    fn gen_stub_uv_write_t() -> uv_write_t {
        ret { fields: { loop_handle: ptr::null(), type_: 0u32,
                        close_cb: ptr::null(),
                        mut data: ptr::null() },
            a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8,
            a03: 0 as *u8,
            a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8,
            a07: 0 as *u8,
            a08: 0 as *u8, a09: 0 as *u8, a10: 0 as *u8,
            a11: 0 as *u8,
            a12: 0 as *u8
        };
    }
    fn gen_stub_uv_getaddrinfo_t() -> uv_getaddrinfo_t {
        {
            a00: 0 as *u8, a01: 0 as *u8, a02: 0 as *u8, a03: 0 as *u8,
            a04: 0 as *u8, a05: 0 as *u8, a06: 0 as *u8, a07: 0 as *u8,
            a08: 0 as *u8
        }
    }
}

#[nolink]
extern mod rustrt {
    // libuv public API
    fn rust_uv_loop_new() -> *libc::c_void;
    fn rust_uv_loop_delete(lp: *libc::c_void);
    fn rust_uv_loop_refcount(loop_ptr: *libc::c_void) -> libc::c_int;
    fn rust_uv_run(loop_handle: *libc::c_void);
    fn rust_uv_close(handle: *libc::c_void, cb: *u8);
    fn rust_uv_async_send(handle: *uv_async_t);
    fn rust_uv_async_init(loop_handle: *libc::c_void,
                          async_handle: *uv_async_t,
                          cb: *u8) -> libc::c_int;
    fn rust_uv_tcp_init(
        loop_handle: *libc::c_void,
        handle_ptr: *uv_tcp_t) -> libc::c_int;
    // FIXME ref #2604 .. ?
    fn rust_uv_buf_init(out_buf: *uv_buf_t, base: *u8,
                        len: libc::size_t);
    fn rust_uv_last_error(loop_handle: *libc::c_void) -> uv_err_t;
    // FIXME ref #2064
    fn rust_uv_strerror(err: *uv_err_t) -> *libc::c_char;
    // FIXME ref #2064
    fn rust_uv_err_name(err: *uv_err_t) -> *libc::c_char;
    fn rust_uv_ip4_addr(ip: *u8, port: libc::c_int)
        -> sockaddr_in;
    fn rust_uv_ip6_addr(ip: *u8, port: libc::c_int)
        -> sockaddr_in6;
    fn rust_uv_ip4_name(src: *sockaddr_in, dst: *u8, size: libc::size_t)
        -> libc::c_int;
    fn rust_uv_ip6_name(src: *sockaddr_in6, dst: *u8, size: libc::size_t)
        -> libc::c_int;
    // FIXME ref #2064
    fn rust_uv_tcp_connect(connect_ptr: *uv_connect_t,
                           tcp_handle_ptr: *uv_tcp_t,
                           ++after_cb: *u8,
                           ++addr: *sockaddr_in) -> libc::c_int;
    // FIXME ref #2064
    fn rust_uv_tcp_bind(tcp_server: *uv_tcp_t,
                        ++addr: *sockaddr_in) -> libc::c_int;
    // FIXME ref #2064
    fn rust_uv_tcp_connect6(connect_ptr: *uv_connect_t,
                           tcp_handle_ptr: *uv_tcp_t,
                           ++after_cb: *u8,
                           ++addr: *sockaddr_in6) -> libc::c_int;
    // FIXME ref #2064
    fn rust_uv_tcp_bind6(tcp_server: *uv_tcp_t,
                        ++addr: *sockaddr_in6) -> libc::c_int;
    fn rust_uv_listen(stream: *libc::c_void, backlog: libc::c_int,
                      cb: *u8) -> libc::c_int;
    fn rust_uv_accept(server: *libc::c_void, client: *libc::c_void)
        -> libc::c_int;
    fn rust_uv_write(req: *libc::c_void, stream: *libc::c_void,
             ++buf_in: *uv_buf_t, buf_cnt: libc::c_int,
             cb: *u8) -> libc::c_int;
    fn rust_uv_read_start(stream: *libc::c_void, on_alloc: *u8,
                          on_read: *u8) -> libc::c_int;
    fn rust_uv_read_stop(stream: *libc::c_void) -> libc::c_int;
    fn rust_uv_timer_init(loop_handle: *libc::c_void,
                          timer_handle: *uv_timer_t) -> libc::c_int;
    fn rust_uv_timer_start(
        timer_handle: *uv_timer_t,
        cb: *u8,
        timeout: libc::c_uint,
        repeat: libc::c_uint) -> libc::c_int;
    fn rust_uv_timer_stop(handle: *uv_timer_t) -> libc::c_int;

    fn rust_uv_getaddrinfo(loop_ptr: *libc::c_void,
                           handle: *uv_getaddrinfo_t,
                           cb: *u8,
                           node_name_ptr: *u8,
                           service_name_ptr: *u8,
                           // should probably only pass ptr::null()
                           hints: *addrinfo) -> libc::c_int;
    fn rust_uv_freeaddrinfo(res: *addrinfo);

    // data accessors/helpers for rust-mapped uv structs
    fn rust_uv_helper_get_INADDR_NONE() -> u32;
    fn rust_uv_is_ipv4_addrinfo(input: *addrinfo) -> bool;
    fn rust_uv_is_ipv6_addrinfo(input: *addrinfo) -> bool;
    fn rust_uv_get_next_addrinfo(input: *addrinfo) -> *addrinfo;
    fn rust_uv_addrinfo_as_sockaddr_in(input: *addrinfo) -> *sockaddr_in;
    fn rust_uv_addrinfo_as_sockaddr_in6(input: *addrinfo) -> *sockaddr_in6;
    fn rust_uv_malloc_buf_base_of(sug_size: libc::size_t) -> *u8;
    fn rust_uv_free_base_of_buf(++buf: uv_buf_t);
    fn rust_uv_get_stream_handle_from_connect_req(
        connect_req: *uv_connect_t)
        -> *uv_stream_t;
    fn rust_uv_get_stream_handle_from_write_req(
        write_req: *uv_write_t)
        -> *uv_stream_t;
    fn rust_uv_get_loop_for_uv_handle(handle: *libc::c_void)
        -> *libc::c_void;
    fn rust_uv_get_data_for_uv_loop(loop_ptr: *libc::c_void) -> *libc::c_void;
    fn rust_uv_set_data_for_uv_loop(loop_ptr: *libc::c_void,
                                    data: *libc::c_void);
    fn rust_uv_get_data_for_uv_handle(handle: *libc::c_void)
        -> *libc::c_void;
    fn rust_uv_set_data_for_uv_handle(handle: *libc::c_void,
                                      data: *libc::c_void);
    fn rust_uv_get_data_for_req(req: *libc::c_void) -> *libc::c_void;
    fn rust_uv_set_data_for_req(req: *libc::c_void,
                                data: *libc::c_void);
    fn rust_uv_get_base_from_buf(++buf: uv_buf_t) -> *u8;
    fn rust_uv_get_len_from_buf(++buf: uv_buf_t) -> libc::size_t;

    // sizeof testing helpers
    fn rust_uv_helper_uv_tcp_t_size() -> libc::c_uint;
    fn rust_uv_helper_uv_connect_t_size() -> libc::c_uint;
    fn rust_uv_helper_uv_buf_t_size() -> libc::c_uint;
    fn rust_uv_helper_uv_write_t_size() -> libc::c_uint;
    fn rust_uv_helper_uv_err_t_size() -> libc::c_uint;
    fn rust_uv_helper_sockaddr_in_size() -> libc::c_uint;
    fn rust_uv_helper_sockaddr_in6_size() -> libc::c_uint;
    fn rust_uv_helper_uv_async_t_size() -> libc::c_uint;
    fn rust_uv_helper_uv_timer_t_size() -> libc::c_uint;
    fn rust_uv_helper_uv_getaddrinfo_t_size() -> libc::c_uint;
    fn rust_uv_helper_addrinfo_size() -> libc::c_uint;
    fn rust_uv_helper_addr_in_size() -> libc::c_uint;
}

unsafe fn loop_new() -> *libc::c_void {
    ret rustrt::rust_uv_loop_new();
}

unsafe fn loop_delete(loop_handle: *libc::c_void) {
    rustrt::rust_uv_loop_delete(loop_handle);
}

unsafe fn loop_refcount(loop_ptr: *libc::c_void) -> libc::c_int {
    ret rustrt::rust_uv_loop_refcount(loop_ptr);
}

unsafe fn run(loop_handle: *libc::c_void) {
    rustrt::rust_uv_run(loop_handle);
}

unsafe fn close<T>(handle: *T, cb: *u8) {
    rustrt::rust_uv_close(handle as *libc::c_void, cb);
}

unsafe fn tcp_init(loop_handle: *libc::c_void, handle: *uv_tcp_t)
    -> libc::c_int {
    ret rustrt::rust_uv_tcp_init(loop_handle, handle);
}
// FIXME ref #2064
unsafe fn tcp_connect(connect_ptr: *uv_connect_t,
                      tcp_handle_ptr: *uv_tcp_t,
                      addr_ptr: *sockaddr_in,
                      ++after_connect_cb: *u8)
-> libc::c_int {
    log(debug, #fmt("b4 foreign tcp_connect--addr port: %u cb: %u",
                    (*addr_ptr).sin_port as uint, after_connect_cb as uint));
    ret rustrt::rust_uv_tcp_connect(connect_ptr, tcp_handle_ptr,
                                    after_connect_cb, addr_ptr);
}
// FIXME ref #2064
unsafe fn tcp_connect6(connect_ptr: *uv_connect_t,
                      tcp_handle_ptr: *uv_tcp_t,
                      addr_ptr: *sockaddr_in6,
                      ++after_connect_cb: *u8)
-> libc::c_int {
    ret rustrt::rust_uv_tcp_connect6(connect_ptr, tcp_handle_ptr,
                                    after_connect_cb, addr_ptr);
}
// FIXME ref #2064
unsafe fn tcp_bind(tcp_server_ptr: *uv_tcp_t,
                   addr_ptr: *sockaddr_in) -> libc::c_int {
    ret rustrt::rust_uv_tcp_bind(tcp_server_ptr,
                                 addr_ptr);
}
// FIXME ref #2064
unsafe fn tcp_bind6(tcp_server_ptr: *uv_tcp_t,
                   addr_ptr: *sockaddr_in6) -> libc::c_int {
    ret rustrt::rust_uv_tcp_bind6(tcp_server_ptr,
                                 addr_ptr);
}

unsafe fn listen<T>(stream: *T, backlog: libc::c_int,
                 cb: *u8) -> libc::c_int {
    ret rustrt::rust_uv_listen(stream as *libc::c_void, backlog, cb);
}

unsafe fn accept(server: *libc::c_void, client: *libc::c_void)
    -> libc::c_int {
    ret rustrt::rust_uv_accept(server as *libc::c_void,
                               client as *libc::c_void);
}

unsafe fn write<T>(req: *uv_write_t, stream: *T,
         buf_in: *~[uv_buf_t], cb: *u8) -> libc::c_int {
    let buf_ptr = vec::unsafe::to_ptr(*buf_in);
    let buf_cnt = vec::len(*buf_in) as i32;
    ret rustrt::rust_uv_write(req as *libc::c_void,
                              stream as *libc::c_void,
                              buf_ptr, buf_cnt, cb);
}
unsafe fn read_start(stream: *uv_stream_t, on_alloc: *u8,
                     on_read: *u8) -> libc::c_int {
    ret rustrt::rust_uv_read_start(stream as *libc::c_void,
                                   on_alloc, on_read);
}

unsafe fn read_stop(stream: *uv_stream_t) -> libc::c_int {
    ret rustrt::rust_uv_read_stop(stream as *libc::c_void);
}

unsafe fn last_error(loop_handle: *libc::c_void) -> uv_err_t {
    ret rustrt::rust_uv_last_error(loop_handle);
}

unsafe fn strerror(err: *uv_err_t) -> *libc::c_char {
    ret rustrt::rust_uv_strerror(err);
}
unsafe fn err_name(err: *uv_err_t) -> *libc::c_char {
    ret rustrt::rust_uv_err_name(err);
}

unsafe fn async_init(loop_handle: *libc::c_void,
                     async_handle: *uv_async_t,
                     cb: *u8) -> libc::c_int {
    ret rustrt::rust_uv_async_init(loop_handle,
                                   async_handle,
                                   cb);
}

unsafe fn async_send(async_handle: *uv_async_t) {
    ret rustrt::rust_uv_async_send(async_handle);
}
unsafe fn buf_init(++input: *u8, len: uint) -> uv_buf_t {
    let out_buf = { base: ptr::null(), len: 0 as libc::size_t };
    let out_buf_ptr = ptr::addr_of(out_buf);
    log(debug, #fmt("buf_init - input %u len %u out_buf: %u",
                     input as uint,
                     len as uint,
                     out_buf_ptr as uint));
    // yuck :/
    rustrt::rust_uv_buf_init(out_buf_ptr, input, len as size_t);
    //let result = rustrt::rust_uv_buf_init_2(input, len as size_t);
    log(debug, "after rust_uv_buf_init");
    let res_base = get_base_from_buf(out_buf);
    let res_len = get_len_from_buf(out_buf);
    //let res_base = get_base_from_buf(result);
    log(debug, #fmt("buf_init - result %u len %u",
                     res_base as uint,
                     res_len as uint));
    ret out_buf;
    //ret result;
}
unsafe fn ip4_addr(ip: str, port: int)
-> sockaddr_in {
    do str::as_c_str(ip) |ip_buf| {
        rustrt::rust_uv_ip4_addr(ip_buf as *u8,
                                 port as libc::c_int)
    }
}
unsafe fn ip6_addr(ip: str, port: int)
-> sockaddr_in6 {
    do str::as_c_str(ip) |ip_buf| {
        rustrt::rust_uv_ip6_addr(ip_buf as *u8,
                                 port as libc::c_int)
    }
}
unsafe fn ip4_name(src: &sockaddr_in) -> str {
    // ipv4 addr max size: 15 + 1 trailing null byte
    let dst: [u8]/~ = [0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,
                     0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8]/~;
    let size = 16 as libc::size_t;
    do vec::as_buf(dst) |dst_buf| {
        rustrt::rust_uv_ip4_name(src as *sockaddr_in,
                                              dst_buf, size);
        // seems that checking the result of uv_ip4_name
        // doesn't work too well..
        // you're stuck looking at the value of dst_buf
        // to see if it is the string representation of
        // INADDR_NONE (0xffffffff or 255.255.255.255 on
        // many platforms)
        str::unsafe::from_buf(dst_buf)
    }
}
unsafe fn ip6_name(src: &sockaddr_in6) -> str {
    // ipv6 addr max size: 45 + 1 trailing null byte
    let dst: [u8]/~ = [0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,
                       0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,
                       0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,
                       0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,
                       0u8,0u8,0u8,0u8,0u8,0u8,0u8,0u8,
                       0u8,0u8,0u8,0u8,0u8,0u8]/~;
    let size = 46 as libc::size_t;
    do vec::as_buf(dst) |dst_buf| {
        let src_unsafe_ptr = src as *sockaddr_in6;
        log(debug, #fmt("val of src *sockaddr_in6: %? sockaddr_in6: %?",
                        src_unsafe_ptr, src));
        let result = rustrt::rust_uv_ip6_name(src_unsafe_ptr,
                                              dst_buf, size);
        alt result {
          0i32 {
            str::unsafe::from_buf(dst_buf)
          }
          _ {
            ""
          }
        }
    }
}

unsafe fn timer_init(loop_ptr: *libc::c_void,
                     timer_ptr: *uv_timer_t) -> libc::c_int {
    ret rustrt::rust_uv_timer_init(loop_ptr, timer_ptr);
}
unsafe fn timer_start(timer_ptr: *uv_timer_t, cb: *u8, timeout: uint,
                      repeat: uint) -> libc::c_int {
    ret rustrt::rust_uv_timer_start(timer_ptr, cb, timeout as libc::c_uint,
                                    repeat as libc::c_uint);
}
unsafe fn timer_stop(timer_ptr: *uv_timer_t) -> libc::c_int {
    ret rustrt::rust_uv_timer_stop(timer_ptr);
}
unsafe fn getaddrinfo(loop_ptr: *libc::c_void,
                           handle: *uv_getaddrinfo_t,
                           cb: *u8,
                           node_name_ptr: *u8,
                           service_name_ptr: *u8,
                           hints: *addrinfo) -> libc::c_int {
    rustrt::rust_uv_getaddrinfo(loop_ptr,
                           handle,
                           cb,
                           node_name_ptr,
                           service_name_ptr,
                           hints)
}
unsafe fn freeaddrinfo(res: *addrinfo) {
    rustrt::rust_uv_freeaddrinfo(res);
}

// libuv struct initializers
unsafe fn tcp_t() -> uv_tcp_t {
    ret uv_ll_struct_stubgen::gen_stub_uv_tcp_t();
}
unsafe fn connect_t() -> uv_connect_t {
    ret uv_ll_struct_stubgen::gen_stub_uv_connect_t();
}
unsafe fn write_t() -> uv_write_t {
    ret uv_ll_struct_stubgen::gen_stub_uv_write_t();
}
unsafe fn async_t() -> uv_async_t {
    ret uv_ll_struct_stubgen::gen_stub_uv_async_t();
}
unsafe fn timer_t() -> uv_timer_t {
    ret uv_ll_struct_stubgen::gen_stub_uv_timer_t();
}
unsafe fn getaddrinfo_t() -> uv_getaddrinfo_t {
    ret uv_ll_struct_stubgen::gen_stub_uv_getaddrinfo_t();
}

// data access helpers
unsafe fn get_loop_for_uv_handle<T>(handle: *T)
    -> *libc::c_void {
    ret rustrt::rust_uv_get_loop_for_uv_handle(handle as *libc::c_void);
}
unsafe fn get_stream_handle_from_connect_req(connect: *uv_connect_t)
    -> *uv_stream_t {
    ret rustrt::rust_uv_get_stream_handle_from_connect_req(
        connect);
}
unsafe fn get_stream_handle_from_write_req(
    write_req: *uv_write_t)
    -> *uv_stream_t {
    ret rustrt::rust_uv_get_stream_handle_from_write_req(
        write_req);
}
unsafe fn get_data_for_uv_loop(loop_ptr: *libc::c_void) -> *libc::c_void {
    rustrt::rust_uv_get_data_for_uv_loop(loop_ptr)
}
unsafe fn set_data_for_uv_loop(loop_ptr: *libc::c_void, data: *libc::c_void) {
    rustrt::rust_uv_set_data_for_uv_loop(loop_ptr, data);
}
unsafe fn get_data_for_uv_handle<T>(handle: *T) -> *libc::c_void {
    ret rustrt::rust_uv_get_data_for_uv_handle(handle as *libc::c_void);
}
unsafe fn set_data_for_uv_handle<T, U>(handle: *T,
                    data: *U) {
    rustrt::rust_uv_set_data_for_uv_handle(handle as *libc::c_void,
                                           data as *libc::c_void);
}
unsafe fn get_data_for_req<T>(req: *T) -> *libc::c_void {
    ret rustrt::rust_uv_get_data_for_req(req as *libc::c_void);
}
unsafe fn set_data_for_req<T, U>(req: *T,
                    data: *U) {
    rustrt::rust_uv_set_data_for_req(req as *libc::c_void,
                                     data as *libc::c_void);
}
unsafe fn get_base_from_buf(buf: uv_buf_t) -> *u8 {
    ret rustrt::rust_uv_get_base_from_buf(buf);
}
unsafe fn get_len_from_buf(buf: uv_buf_t) -> libc::size_t {
    ret rustrt::rust_uv_get_len_from_buf(buf);
}
unsafe fn malloc_buf_base_of(suggested_size: libc::size_t)
    -> *u8 {
    ret rustrt::rust_uv_malloc_buf_base_of(suggested_size);
}
unsafe fn free_base_of_buf(buf: uv_buf_t) {
    rustrt::rust_uv_free_base_of_buf(buf);
}

unsafe fn get_last_err_info(uv_loop: *libc::c_void) -> str {
    let err = last_error(uv_loop);
    let err_ptr = ptr::addr_of(err);
    let err_name = str::unsafe::from_c_str(err_name(err_ptr));
    let err_msg = str::unsafe::from_c_str(strerror(err_ptr));
    ret #fmt("LIBUV ERROR: name: %s msg: %s",
                    err_name, err_msg);
}

unsafe fn get_last_err_data(uv_loop: *libc::c_void) -> uv_err_data {
    let err = last_error(uv_loop);
    let err_ptr = ptr::addr_of(err);
    let err_name = str::unsafe::from_c_str(err_name(err_ptr));
    let err_msg = str::unsafe::from_c_str(strerror(err_ptr));
    { err_name: err_name, err_msg: err_msg }
}

type uv_err_data = {
    err_name: str,
    err_msg: str
};

unsafe fn is_ipv4_addrinfo(input: *addrinfo) -> bool {
    rustrt::rust_uv_is_ipv4_addrinfo(input)
}
unsafe fn is_ipv6_addrinfo(input: *addrinfo) -> bool {
    rustrt::rust_uv_is_ipv6_addrinfo(input)
}
unsafe fn get_INADDR_NONE() -> u32 {
    rustrt::rust_uv_helper_get_INADDR_NONE()
}
unsafe fn get_next_addrinfo(input: *addrinfo) -> *addrinfo {
    rustrt::rust_uv_get_next_addrinfo(input)
}
unsafe fn addrinfo_as_sockaddr_in(input: *addrinfo) -> *sockaddr_in {
    rustrt::rust_uv_addrinfo_as_sockaddr_in(input)
}
unsafe fn addrinfo_as_sockaddr_in6(input: *addrinfo) -> *sockaddr_in6 {
    rustrt::rust_uv_addrinfo_as_sockaddr_in6(input)
}

#[cfg(test)]
mod test {
    enum tcp_read_data {
        tcp_read_eof,
        tcp_read_more(~[u8]),
        tcp_read_error
    }

    type request_wrapper = {
        write_req: *uv_write_t,
        req_buf: *~[uv_buf_t],
        read_chan: *comm::chan<str>
    };

    extern fn after_close_cb(handle: *libc::c_void) {
        log(debug, #fmt("after uv_close! handle ptr: %?",
                        handle));
    }

    extern fn on_alloc_cb(handle: *libc::c_void,
                         ++suggested_size: libc::size_t)
        -> uv_buf_t unsafe {
        log(debug, "on_alloc_cb!");
        let char_ptr = malloc_buf_base_of(suggested_size);
        log(debug, #fmt("on_alloc_cb h: %? char_ptr: %u sugsize: %u",
                         handle,
                         char_ptr as uint,
                         suggested_size as uint));
        ret buf_init(char_ptr, suggested_size as uint);
    }

    extern fn on_read_cb(stream: *uv_stream_t,
                        nread: libc::ssize_t,
                        ++buf: uv_buf_t) unsafe {
        let nread = nread as int;
        log(debug, #fmt("CLIENT entering on_read_cb nred: %d",
                        nread));
        if (nread > 0) {
            // we have data
            log(debug, #fmt("CLIENT read: data! nread: %d", nread));
            read_stop(stream);
            let client_data =
                get_data_for_uv_handle(stream as *libc::c_void)
                  as *request_wrapper;
            let buf_base = get_base_from_buf(buf);
            let buf_len = get_len_from_buf(buf);
            let bytes = vec::unsafe::from_buf(buf_base, buf_len as uint);
            let read_chan = *((*client_data).read_chan);
            let msg_from_server = str::from_bytes(bytes);
            comm::send(read_chan, msg_from_server);
            close(stream as *libc::c_void, after_close_cb)
        }
        else if (nread == -1) {
            // err .. possibly EOF
            log(debug, "read: eof!");
        }
        else {
            // nread == 0 .. do nothing, just free buf as below
            log(debug, "read: do nothing!");
        }
        // when we're done
        free_base_of_buf(buf);
        log(debug, "CLIENT exiting on_read_cb");
    }

    extern fn on_write_complete_cb(write_req: *uv_write_t,
                                  status: libc::c_int) unsafe {
        log(debug, #fmt("CLIENT beginning on_write_complete_cb status: %d",
                         status as int));
        let stream = get_stream_handle_from_write_req(write_req);
        log(debug, #fmt("CLIENT on_write_complete_cb: tcp:%d write_handle:%d",
            stream as int, write_req as int));
        let result = read_start(stream, on_alloc_cb, on_read_cb);
        log(debug, #fmt("CLIENT ending on_write_complete_cb .. status: %d",
                         result as int));
    }

    extern fn on_connect_cb(connect_req_ptr: *uv_connect_t,
                                 status: libc::c_int) unsafe {
        log(debug, #fmt("beginning on_connect_cb .. status: %d",
                         status as int));
        let stream =
            get_stream_handle_from_connect_req(connect_req_ptr);
        if (status == 0i32) {
            log(debug, "on_connect_cb: in status=0 if..");
            let client_data = get_data_for_req(
                connect_req_ptr as *libc::c_void)
                as *request_wrapper;
            let write_handle = (*client_data).write_req;
            log(debug, #fmt("on_connect_cb: tcp: %d write_hdl: %d",
                            stream as int, write_handle as int));
            let write_result = write(write_handle,
                              stream as *libc::c_void,
                              (*client_data).req_buf,
                              on_write_complete_cb);
            log(debug, #fmt("on_connect_cb: write() status: %d",
                             write_result as int));
        }
        else {
            let test_loop = get_loop_for_uv_handle(
                stream as *libc::c_void);
            let err_msg = get_last_err_info(test_loop);
            log(debug, err_msg);
            assert false;
        }
        log(debug, "finishing on_connect_cb");
    }

    fn impl_uv_tcp_request(ip: str, port: int, req_str: str,
                          client_chan: *comm::chan<str>) unsafe {
        let test_loop = loop_new();
        let tcp_handle = tcp_t();
        let tcp_handle_ptr = ptr::addr_of(tcp_handle);
        let connect_handle = connect_t();
        let connect_req_ptr = ptr::addr_of(connect_handle);

        // this is the persistent payload of data that we
        // need to pass around to get this example to work.
        // In C, this would be a malloc'd or stack-allocated
        // struct that we'd cast to a void* and store as the
        // data field in our uv_connect_t struct
        let req_str_bytes = str::bytes(req_str);
        let req_msg_ptr: *u8 = vec::unsafe::to_ptr(req_str_bytes);
        log(debug, #fmt("req_msg ptr: %u", req_msg_ptr as uint));
        let req_msg = ~[
            buf_init(req_msg_ptr, vec::len(req_str_bytes))
        ];
        // this is the enclosing record, we'll pass a ptr to
        // this to C..
        let write_handle = write_t();
        let write_handle_ptr = ptr::addr_of(write_handle);
        log(debug, #fmt("tcp req: tcp stream: %d write_handle: %d",
                         tcp_handle_ptr as int,
                         write_handle_ptr as int));
        let client_data = { writer_handle: write_handle_ptr,
                    req_buf: ptr::addr_of(req_msg),
                    read_chan: client_chan };

        let tcp_init_result = tcp_init(
            test_loop as *libc::c_void, tcp_handle_ptr);
        if (tcp_init_result == 0i32) {
            log(debug, "sucessful tcp_init_result");

            log(debug, "building addr...");
            let addr = ip4_addr(ip, port);
            // FIXME ref #2064
            let addr_ptr = ptr::addr_of(addr);
            log(debug, #fmt("after build addr in rust. port: %u",
                             addr.sin_port as uint));

            // this should set up the connection request..
            log(debug, #fmt("b4 call tcp_connect connect cb: %u ",
                            on_connect_cb as uint));
            let tcp_connect_result = tcp_connect(
                connect_req_ptr, tcp_handle_ptr,
                addr_ptr, on_connect_cb);
            if (tcp_connect_result == 0i32) {
                // not set the data on the connect_req
                // until its initialized
                set_data_for_req(
                    connect_req_ptr as *libc::c_void,
                    ptr::addr_of(client_data) as *libc::c_void);
                set_data_for_uv_handle(
                    tcp_handle_ptr as *libc::c_void,
                    ptr::addr_of(client_data) as *libc::c_void);
                log(debug, "before run tcp req loop");
                run(test_loop);
                log(debug, "after run tcp req loop");
            }
            else {
               log(debug, "tcp_connect() failure");
               assert false;
            }
        }
        else {
            log(debug, "tcp_init() failure");
            assert false;
        }
        loop_delete(test_loop);

    }

    extern fn server_after_close_cb(handle: *libc::c_void) unsafe {
        log(debug, #fmt("SERVER server stream closed, should exit.. h: %?",
                   handle));
    }

    extern fn client_stream_after_close_cb(handle: *libc::c_void)
        unsafe {
        log(debug, "SERVER: closed client stream, now closing server stream");
        let client_data = get_data_for_uv_handle(
            handle) as
            *tcp_server_data;
        close((*client_data).server as *libc::c_void,
                      server_after_close_cb);
    }

    extern fn after_server_resp_write(req: *uv_write_t) unsafe {
        let client_stream_ptr =
            get_stream_handle_from_write_req(req);
        log(debug, "SERVER: resp sent... closing client stream");
        close(client_stream_ptr as *libc::c_void,
                      client_stream_after_close_cb)
    }

    extern fn on_server_read_cb(client_stream_ptr: *uv_stream_t,
                               nread: libc::ssize_t,
                               ++buf: uv_buf_t) unsafe {
        let nread = nread as int;
        if (nread > 0) {
            // we have data
            log(debug, #fmt("SERVER read: data! nread: %d", nread));

            // pull out the contents of the write from the client
            let buf_base = get_base_from_buf(buf);
            let buf_len = get_len_from_buf(buf) as uint;
            log(debug, #fmt("SERVER buf base: %u, len: %u, nread: %d",
                            buf_base as uint,
                            buf_len as uint,
                            nread));
            let bytes = vec::unsafe::from_buf(buf_base, buf_len);
            let request_str = str::from_bytes(bytes);

            let client_data = get_data_for_uv_handle(
                client_stream_ptr as *libc::c_void) as *tcp_server_data;

            let server_kill_msg = (*client_data).server_kill_msg;
            let write_req = (*client_data).server_write_req;
            if (str::contains(request_str, server_kill_msg)) {
                log(debug, "SERVER: client req contains kill_msg!");
                log(debug, "SERVER: sending response to client");
                read_stop(client_stream_ptr);
                let server_chan = *((*client_data).server_chan);
                comm::send(server_chan, request_str);
                let write_result = write(
                    write_req,
                    client_stream_ptr as *libc::c_void,
                    (*client_data).server_resp_buf,
                    after_server_resp_write);
                log(debug, #fmt("SERVER: resp write result: %d",
                            write_result as int));
                if (write_result != 0i32) {
                    log(debug, "bad result for server resp write()");
                    log(debug, get_last_err_info(
                        get_loop_for_uv_handle(client_stream_ptr
                            as *libc::c_void)));
                    assert false;
                }
            }
            else {
                log(debug, "SERVER: client req !contain kill_msg!");
            }
        }
        else if (nread == -1) {
            // err .. possibly EOF
            log(debug, "read: eof!");
        }
        else {
            // nread == 0 .. do nothing, just free buf as below
            log(debug, "read: do nothing!");
        }
        // when we're done
        free_base_of_buf(buf);
        log(debug, "SERVER exiting on_read_cb");
    }

    extern fn server_connection_cb(server_stream_ptr:
                                    *uv_stream_t,
                                  status: libc::c_int) unsafe {
        log(debug, "client connecting!");
        let test_loop = get_loop_for_uv_handle(
                               server_stream_ptr as *libc::c_void);
        if status != 0i32 {
            let err_msg = get_last_err_info(test_loop);
            log(debug, #fmt("server_connect_cb: non-zero status: %?",
                         err_msg));
            ret;
        }
        let server_data = get_data_for_uv_handle(
            server_stream_ptr as *libc::c_void) as *tcp_server_data;
        let client_stream_ptr = (*server_data).client;
        let client_init_result = tcp_init(test_loop,
                                                  client_stream_ptr);
        set_data_for_uv_handle(
            client_stream_ptr as *libc::c_void,
            server_data as *libc::c_void);
        if (client_init_result == 0i32) {
            log(debug, "successfully initialized client stream");
            let accept_result = accept(server_stream_ptr as
                                                 *libc::c_void,
                                               client_stream_ptr as
                                                 *libc::c_void);
            if (accept_result == 0i32) {
                // start reading
                let read_result = read_start(
                    client_stream_ptr as *uv_stream_t,
                                                     on_alloc_cb,
                                                     on_server_read_cb);
                if (read_result == 0i32) {
                    log(debug, "successful server read start");
                }
                else {
                    log(debug, #fmt("server_connection_cb: bad read:%d",
                                    read_result as int));
                    assert false;
                }
            }
            else {
                log(debug, #fmt("server_connection_cb: bad accept: %d",
                            accept_result as int));
                assert false;
            }
        }
        else {
            log(debug, #fmt("server_connection_cb: bad client init: %d",
                        client_init_result as int));
            assert false;
        }
    }

    type tcp_server_data = {
        client: *uv_tcp_t,
        server: *uv_tcp_t,
        server_kill_msg: str,
        server_resp_buf: *~[uv_buf_t],
        server_chan: *comm::chan<str>,
        server_write_req: *uv_write_t
    };

    type async_handle_data = {
        continue_chan: *comm::chan<bool>
    };

    extern fn async_close_cb(handle: *libc::c_void) {
        log(debug, #fmt("SERVER: closing async cb... h: %?",
                   handle));
    }

    extern fn continue_async_cb(async_handle: *uv_async_t,
                               status: libc::c_int) unsafe {
        // once we're in the body of this callback,
        // the tcp server's loop is set up, so we
        // can continue on to let the tcp client
        // do its thang
        let data = get_data_for_uv_handle(
            async_handle as *libc::c_void) as *async_handle_data;
        let continue_chan = *((*data).continue_chan);
        let should_continue = status == 0i32;
        comm::send(continue_chan, should_continue);
        close(async_handle as *libc::c_void, async_close_cb);
    }

    fn impl_uv_tcp_server(server_ip: str,
                          server_port: int,
                          kill_server_msg: str,
                          server_resp_msg: str,
                          server_chan: *comm::chan<str>,
                          continue_chan: *comm::chan<bool>) unsafe {
        let test_loop = loop_new();
        let tcp_server = tcp_t();
        let tcp_server_ptr = ptr::addr_of(tcp_server);

        let tcp_client = tcp_t();
        let tcp_client_ptr = ptr::addr_of(tcp_client);

        let server_write_req = write_t();
        let server_write_req_ptr = ptr::addr_of(server_write_req);

        let resp_str_bytes = str::bytes(server_resp_msg);
        let resp_msg_ptr: *u8 = vec::unsafe::to_ptr(resp_str_bytes);
        log(debug, #fmt("resp_msg ptr: %u", resp_msg_ptr as uint));
        let resp_msg = ~[
            buf_init(resp_msg_ptr, vec::len(resp_str_bytes))
        ];

        let continue_async_handle = async_t();
        let continue_async_handle_ptr =
            ptr::addr_of(continue_async_handle);
        let async_data =
            { continue_chan: continue_chan };
        let async_data_ptr = ptr::addr_of(async_data);

        let server_data: tcp_server_data = {
            client: tcp_client_ptr,
            server: tcp_server_ptr,
            server_kill_msg: kill_server_msg,
            server_resp_buf: ptr::addr_of(resp_msg),
            server_chan: server_chan,
            server_write_req: server_write_req_ptr
        };
        let server_data_ptr = ptr::addr_of(server_data);
        set_data_for_uv_handle(tcp_server_ptr as *libc::c_void,
                                       server_data_ptr as *libc::c_void);

        // uv_tcp_init()
        let tcp_init_result = tcp_init(
            test_loop as *libc::c_void, tcp_server_ptr);
        if (tcp_init_result == 0i32) {
            let server_addr = ip4_addr(server_ip, server_port);
            // FIXME ref #2064
            let server_addr_ptr = ptr::addr_of(server_addr);

            // uv_tcp_bind()
            let bind_result = tcp_bind(tcp_server_ptr,
                                               server_addr_ptr);
            if (bind_result == 0i32) {
                log(debug, "successful uv_tcp_bind, listening");

                // uv_listen()
                let listen_result = listen(tcp_server_ptr as
                                                     *libc::c_void,
                                                   128i32,
                                                   server_connection_cb);
                if (listen_result == 0i32) {
                    // let the test know it can set up the tcp server,
                    // now.. this may still present a race, not sure..
                    let async_result = async_init(test_loop,
                                       continue_async_handle_ptr,
                                       continue_async_cb);
                    if (async_result == 0i32) {
                        set_data_for_uv_handle(
                            continue_async_handle_ptr as *libc::c_void,
                            async_data_ptr as *libc::c_void);
                        async_send(continue_async_handle_ptr);
                        // uv_run()
                        run(test_loop);
                        log(debug, "server uv::run() has returned");
                    }
                    else {
                        log(debug, #fmt("uv_async_init failure: %d",
                                async_result as int));
                        assert false;
                    }
                }
                else {
                    log(debug, #fmt("non-zero result on uv_listen: %d",
                                listen_result as int));
                    assert false;
                }
            }
            else {
                log(debug, #fmt("non-zero result on uv_tcp_bind: %d",
                            bind_result as int));
                assert false;
            }
        }
        else {
            log(debug, #fmt("non-zero result on uv_tcp_init: %d",
                        tcp_init_result as int));
            assert false;
        }
        loop_delete(test_loop);
    }

    // this is the impl for a test that is (maybe) ran on a
    // per-platform/arch basis below
    fn impl_uv_tcp_server_and_request() unsafe {
        let bind_ip = "0.0.0.0";
        let request_ip = "127.0.0.1";
        let port = 8887;
        let kill_server_msg = "does a dog have buddha nature?";
        let server_resp_msg = "mu!";
        let client_port = comm::port::<str>();
        let client_chan = comm::chan::<str>(client_port);
        let server_port = comm::port::<str>();
        let server_chan = comm::chan::<str>(server_port);

        let continue_port = comm::port::<bool>();
        let continue_chan = comm::chan::<bool>(continue_port);
        let continue_chan_ptr = ptr::addr_of(continue_chan);

        do task::spawn_sched(task::manual_threads(1u)) {
            impl_uv_tcp_server(bind_ip, port,
                               kill_server_msg,
                               server_resp_msg,
                               ptr::addr_of(server_chan),
                               continue_chan_ptr);
        };

        // block until the server up is.. possibly a race?
        log(debug, "before receiving on server continue_port");
        comm::recv(continue_port);
        log(debug, "received on continue port, set up tcp client");

        do task::spawn_sched(task::manual_threads(1u)) {
            impl_uv_tcp_request(request_ip, port,
                               kill_server_msg,
                               ptr::addr_of(client_chan));
        };

        let msg_from_client = comm::recv(server_port);
        let msg_from_server = comm::recv(client_port);

        assert str::contains(msg_from_client, kill_server_msg);
        assert str::contains(msg_from_server, server_resp_msg);
    }

    // FIXME don't run on fbsd or linux 32 bit(#2064)
    #[cfg(target_os="win32")]
    #[cfg(target_os="darwin")]
    #[cfg(target_os="linux")]
    mod tcp_and_server_client_test {
        #[cfg(target_arch="x86_64")]
        mod impl64 {
            #[test]
            fn test_uv_ll_tcp_server_and_request() unsafe {
                impl_uv_tcp_server_and_request();
            }
        }
        #[cfg(target_arch="x86")]
        mod impl32 {
            #[test]
            #[ignore(cfg(target_os = "linux"))]
            fn test_uv_ll_tcp_server_and_request() unsafe {
                impl_uv_tcp_server_and_request();
            }
        }
    }

    // struct size tests
    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    fn test_uv_ll_struct_size_uv_tcp_t() {
        let foreign_handle_size = rustrt::rust_uv_helper_uv_tcp_t_size();
        let rust_handle_size = sys::size_of::<uv_tcp_t>();
        let output = #fmt("uv_tcp_t -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }
    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    fn test_uv_ll_struct_size_uv_connect_t() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_uv_connect_t_size();
        let rust_handle_size = sys::size_of::<uv_connect_t>();
        let output = #fmt("uv_connect_t -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }
    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    fn test_uv_ll_struct_size_uv_buf_t() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_uv_buf_t_size();
        let rust_handle_size = sys::size_of::<uv_buf_t>();
        let output = #fmt("uv_buf_t -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }
    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    fn test_uv_ll_struct_size_uv_write_t() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_uv_write_t_size();
        let rust_handle_size = sys::size_of::<uv_write_t>();
        let output = #fmt("uv_write_t -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }

    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    fn test_uv_ll_struct_size_sockaddr_in() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_sockaddr_in_size();
        let rust_handle_size = sys::size_of::<sockaddr_in>();
        let output = #fmt("sockaddr_in -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }
    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    fn test_uv_ll_struct_size_sockaddr_in6() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_sockaddr_in6_size();
        let rust_handle_size = sys::size_of::<sockaddr_in6>();
        let output = #fmt("sockaddr_in6 -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        // FIXME #1645 .. rust appears to pad structs to the nearest byte..?
        // .. can't get the uv::ll::sockaddr_in6 to == 28 :/
        // .. so the type always appears to be 32 in size.. which is
        // good, i guess.. better too big than too little
        assert (4u+foreign_handle_size as uint) == rust_handle_size;
    }
    #[test]
    #[ignore(reason = "questionable size calculations")]
    fn test_uv_ll_struct_size_addr_in() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_addr_in_size();
        let rust_handle_size = sys::size_of::<addr_in>();
        let output = #fmt("addr_in -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        // FIXME #1645 .. see note above about struct padding
        assert (4u+foreign_handle_size as uint) == rust_handle_size;
    }

    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    fn test_uv_ll_struct_size_uv_async_t() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_uv_async_t_size();
        let rust_handle_size = sys::size_of::<uv_async_t>();
        let output = #fmt("uv_async_t -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }

    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    fn test_uv_ll_struct_size_uv_timer_t() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_uv_timer_t_size();
        let rust_handle_size = sys::size_of::<uv_timer_t>();
        let output = #fmt("uv_timer_t -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }

    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    #[ignore(cfg(target_os = "win32"))]
    fn test_uv_ll_struct_size_uv_getaddrinfo_t() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_uv_getaddrinfo_t_size();
        let rust_handle_size = sys::size_of::<uv_getaddrinfo_t>();
        let output = #fmt("uv_getaddrinfo_t -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }
    #[test]
    #[ignore(cfg(target_os = "freebsd"))]
    #[ignore(cfg(target_os = "macos"))]
    #[ignore(cfg(target_os = "win32"))]
    fn test_uv_ll_struct_size_addrinfo() {
        let foreign_handle_size =
            rustrt::rust_uv_helper_addrinfo_size();
        let rust_handle_size = sys::size_of::<addrinfo>();
        let output = #fmt("addrinfo -- foreign: %u rust: %u",
                          foreign_handle_size as uint, rust_handle_size);
        log(debug, output);
        assert foreign_handle_size as uint == rust_handle_size;
    }
}
