    const std = @import("../../std.zig");
    const builtin = @import("builtin");
    const net = @import("net.zig");

    const os = std.os;
    const fmt = std.fmt;
    const mem = std.mem;
    const time = std.time;
    const meta = std.meta;
    const native_os = builtin.os;
    const native_endian = builtin.cpu.arch.endian();

    const Buffer = std.x.os.Buffer;

    const assert = std.debug.assert;

    /// A generic, cross-platform socket abstraction.
    pub const Socket = struct {
        /// A socket-address pair.
        pub const Connection = struct {
            socket: Socket,
            address: Socket.Address,

            /// Enclose a socket and address into a socket-address pair.
            pub fn from(socket: Socket, address: Socket.Address) Socket.Connection {
                return .{ .socket = socket, .address = address };
            }
        };

        /// A generic socket address abstraction. It is safe to directly access and modify
        /// the fields of a `Socket.Address`.
        pub const Address = union(enum) {
            pub const Native = struct {
                pub const requires_prepended_length = native_os.getVersionRange() == .semver;
                pub const Length = if (requires_prepended_length) u8 else [0]u8;

                pub const Family = if (requires_prepended_length) u8 else c_ushort;

                /// POSIX `sockaddr.storage`. The expected size and alignment is specified in IETF RFC 2553.
                pub const Storage = extern struct {
                    pub const expected_size = os.sockaddr.SS_MAXSIZE;
                    pub const expected_alignment = 8;

                    pub const padding_size = expected_size -
                        mem.alignForward(@sizeOf(Address.Native.Length), expected_alignment) -
                        mem.alignForward(@sizeOf(Address.Native.Family), expected_alignment);

                    len: Address.Native.Length align(expected_alignment) = undefined,
                    family: Address.Native.Family align(expected_alignment) = undefined,
                    padding: [padding_size]u8 align(expected_alignment) = undefined,

                    comptime {
                        assert(@sizeOf(Storage) == Storage.expected_size);
                        assert(@alignOf(Storage) == Storage.expected_alignment);
                    }
                };
            };

            ipv4: net.IPv4.Address,
            ipv6: net.IPv6.Address,

            /// Instantiate a new address with a IPv4 host and port.
            pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address {
                return .{ .ipv4 = .{ .host = host, .port = port } };
            }

            /// Instantiate a new address with a IPv6 host and port.
            pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address {
                return .{ .ipv6 = .{ .host = host, .port = port } };
            }

            /// Parses a `sockaddr` into a generic socket address.
            pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address {
                switch (address.family) {
                    os.AF.INET => {
                        const info = @ptrCast(*const os.sockaddr.in, address);
                        const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) };
                        const port = mem.bigToNative(u16, info.port);
                        return Socket.Address.initIPv4(host, port);
                    },
                    os.AF.INET6 => {
                        const info = @ptrCast(*const os.sockaddr.in6, address);
                        const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id };
                        const port = mem.bigToNative(u16, info.port);
                        return Socket.Address.initIPv6(host, port);
                    },
                    else => unreachable,
                }
            }

            /// Encodes a generic socket address into an extern union that may be reliably
            /// casted into a `sockaddr` which may be passed into socket syscalls.
            pub fn toNative(self: Socket.Address) extern union {
                ipv4: os.sockaddr.in,
                ipv6: os.sockaddr.in6,
            } {
                return switch (self) {
                    .ipv4 => |address| .{
                        .ipv4 = .{
                            .addr = @bitCast(u32, address.host.octets),
                            .port = mem.nativeToBig(u16, address.port),
                        },
                    },
                    .ipv6 => |address| .{
                        .ipv6 = .{
                            .addr = address.host.octets,
                            .port = mem.nativeToBig(u16, address.port),
                            .scope_id = address.host.scope_id,
                            .flowinfo = 0,
                        },
                    },
                };
            }

            /// Returns the number of bytes that make up the `sockaddr` equivalent to the address.
            pub fn getNativeSize(self: Socket.Address) u32 {
                return switch (self) {
                    .ipv4 => @sizeOf(os.sockaddr.in),
                    .ipv6 => @sizeOf(os.sockaddr.in6),
                };
            }

            /// Implements the `std.fmt.format` API.
            pub fn format(
                self: Socket.Address,
                comptime layout: []const u8,
                opts: fmt.FormatOptions,
                writer: anytype,
            ) !void {
                _ = opts;
                _ = layout;
                switch (self) {
                    .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
                    .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
                }
            }
        };

        /// POSIX `msghdr`. Denotes a destination address, set of buffers, control data, and flags. Ported
        /// directly from musl.
        pub const Message = if (native_os.isAtLeast(.windows, .vista) != null and native_os.isAtLeast(.windows, .vista).?)
            extern struct {
                name: usize = @ptrToInt(@as(?[*]u8, null)),
                name_len: c_int = 0,

                buffers: usize = undefined,
                buffers_len: c_ulong = undefined,

                control: Buffer = .{
                    .ptr = @ptrToInt(@as(?[*]u8, null)),
                    .len = 0,
                },
                flags: c_ulong = 0,

                pub usingnamespace MessageMixin(Message);
            }
        else if (native_os.tag == .windows)
            extern struct {
                name: usize = @ptrToInt(@as(?[*]u8, null)),
                name_len: c_int = 0,

                buffers: usize = undefined,
                buffers_len: u32 = undefined,

                control: Buffer = .{
                    .ptr = @ptrToInt(@as(?[*]u8, null)),
                    .len = 0,
                },
                flags: u32 = 0,

                pub usingnamespace MessageMixin(Message);
            }
        else if (@sizeOf(usize) > 4 and native_endian == .Big)
            extern struct {
                name: usize = @ptrToInt(@as(?[*]u8, null)),
                name_len: c_uint = 0,

                buffers: usize = undefined,
                _pad_1: c_int = 0,
                buffers_len: c_int = undefined,

                control: usize = @ptrToInt(@as(?[*]u8, null)),
                _pad_2: c_int = 0,
                control_len: c_uint = 0,

                flags: c_int = 0,

                pub usingnamespace MessageMixin(Message);
            }
        else if (@sizeOf(usize) > 4 and native_endian == .Little)
            extern struct {
                name: usize = @ptrToInt(@as(?[*]u8, null)),
                name_len: c_uint = 0,

                buffers: usize = undefined,
                buffers_len: c_int = undefined,
                _pad_1: c_int = 0,

                control: usize = @ptrToInt(@as(?[*]u8, null)),
                control_len: c_uint = 0,
                _pad_2: c_int = 0,

                flags: c_int = 0,

                pub usingnamespace MessageMixin(Message);
            }
        else
            extern struct {
                name: usize = @ptrToInt(@as(?[*]u8, null)),
                name_len: c_uint = 0,

                buffers: usize = undefined,
                buffers_len: c_int = undefined,

                control: usize = @ptrToInt(@as(?[*]u8, null)),
                control_len: c_uint = 0,

                flags: c_int = 0,

                pub usingnamespace MessageMixin(Message);
            };

        fn MessageMixin(comptime Self: type) type {
            return struct {
                pub fn fromBuffers(buffers: []const Buffer) Self {
                    var self: Self = .{};
                    self.setBuffers(buffers);
                    return self;
                }

                pub fn setName(self: *Self, name: []const u8) void {
                    self.name = @ptrToInt(name.ptr);
                    self.name_len = @intCast(meta.fieldInfo(Self, .name_len).field_type, name.len);
                }

                pub fn setBuffers(self: *Self, buffers: []const Buffer) void {
                    self.buffers = @ptrToInt(buffers.ptr);
                    self.buffers_len = @intCast(meta.fieldInfo(Self, .buffers_len).field_type, buffers.len);
                }

                pub fn setControl(self: *Self, control: []const u8) void {
                    if (native_os.tag == .windows) {
                        self.control = Buffer.from(control);
                    } else {
                        self.control = @ptrToInt(control.ptr);
                        self.control_len = @intCast(meta.fieldInfo(Self, .control_len).field_type, control.len);
                    }
                }

                pub fn setFlags(self: *Self, flags: u32) void {
                    self.flags = @intCast(meta.fieldInfo(Self, .flags).field_type, flags);
                }

                pub fn getName(self: Self) []const u8 {
                    return @intToPtr([*]const u8, self.name)[0..@intCast(usize, self.name_len)];
                }

                pub fn getBuffers(self: Self) []const Buffer {
                    return @intToPtr([*]const Buffer, self.buffers)[0..@intCast(usize, self.buffers_len)];
                }

                pub fn getControl(self: Self) []const u8 {
                    if (native_os.tag == .windows) {
                        return self.control.into();
                    } else {
                        return @intToPtr([*]const u8, self.control)[0..@intCast(usize, self.control_len)];
                    }
                }

                pub fn getFlags(self: Self) u32 {
                    return @intCast(u32, self.flags);
                }
            };
        }

        /// POSIX `linger`, denoting the linger settings of a socket.
        ///
        /// Microsoft's documentation and glibc denote the fields to be unsigned
        /// short's on Windows, whereas glibc and musl denote the fields to be
        /// int's on every other platform.
        pub const Linger = extern struct {
            pub const Field = switch (native_os.tag) {
                .windows => c_ushort,
                else => c_int,
            };

            enabled: Field,
            timeout_seconds: Field,

            pub fn init(timeout_seconds: ?u16) Socket.Linger {
                return .{
                    .enabled = @intCast(Socket.Linger.Field, @boolToInt(timeout_seconds != null)),
                    .timeout_seconds = if (timeout_seconds) |seconds| @intCast(Socket.Linger.Field, seconds) else 0,
                };
            }
        };

        /// Possible set of flags to initialize a socket with.
        pub const InitFlags = enum {
            // Initialize a socket to be non-blocking.

            nonblocking,

            // Have a socket close itself on exec syscalls.

            close_on_exec,
        };

        /// The underlying handle of a socket.
        fd: os.socket_t,

        /// Enclose a socket abstraction over an existing socket file descriptor.
        pub fn from(fd: os.socket_t) Socket {
            return Socket{ .fd = fd };
        }

        /// Mix in socket syscalls depending on the platform we are compiling against.
        pub usingnamespace switch (native_os.tag) {
            .windows => @import("socket_windows.zig"),
            else => @import("socket_posix.zig"),
        }.Mixin(Socket);
    };