package zmq import "core:c" import "core:os" // ZMQ_HAUSNUMERO — base offset for ZMQ's private error codes. // // zmq.h defines two ranges on top of this base: // +1..18 — fallback POSIX errnos (ENOTSUP, EPROTONOSUPPORT, ... ENETRESET). // Only used when the platform's C lacks the symbol // (e.g. MSVC on Windows). On Linux/macOS the system's native // errno values are used instead, so these never appear from // zmq_errno() — but we normalize to them for portability (see // normalize_to_zmq). // +51..54 — ZMQ-native codes (EFSM, ENOCOMPATPROTO, ETERM, EMTHREAD). // Always ZMQ-specific, defined unconditionally. // // The value is fixed by libzmq's ABI-stability promise (zmq.h): "the value // of constants must not change, and old values may not be reused". A C // #define is not a linkable symbol, so hardcoding is the only option. HAUSNUMERO :: 156384712 ZMQ_Error :: enum c.int { // Fallback POSIX errnos — ZMQ's own values for these errnos, used on // platforms where the C doesn't define them. We normalize // all platforms to these values via normalize_to_zmq so that the same // logical error always has the same value and lands in ZMQ_Error // regardless of platform. ENOTSUP = HAUSNUMERO + 1, EPROTONOSUPPORT = HAUSNUMERO + 2, ENOBUFS = HAUSNUMERO + 3, ENETDOWN = HAUSNUMERO + 4, EADDRINUSE = HAUSNUMERO + 5, EADDRNOTAVAIL = HAUSNUMERO + 6, ECONNREFUSED = HAUSNUMERO + 7, EINPROGRESS = HAUSNUMERO + 8, ENOTSOCK = HAUSNUMERO + 9, EMSGSIZE = HAUSNUMERO + 10, EAFNOSUPPORT = HAUSNUMERO + 11, ENETUNREACH = HAUSNUMERO + 12, ECONNABORTED = HAUSNUMERO + 13, ECONNRESET = HAUSNUMERO + 14, ENOTCONN = HAUSNUMERO + 15, ETIMEDOUT = HAUSNUMERO + 16, EHOSTUNREACH = HAUSNUMERO + 17, ENETRESET = HAUSNUMERO + 18, // ZMQ-native error codes. EFSM = HAUSNUMERO + 51, ENOCOMPATPROTO = HAUSNUMERO + 52, ETERM = HAUSNUMERO + 53, EMTHREAD = HAUSNUMERO + 54, } // Map platform-native POSIX errnos to ZMQ fallback values. // // The 18 errnos below have different numeric values on different platforms // (e.g. EADDRINUSE is 98 on Linux, 100 on Windows, 48 on macOS). By // translating them to ZMQ's canonical fallback values (HAUSNUMERO+offset), // the same logical error always lands in ZMQ_Error with the same value // on every platform — enabling portable pattern matching. // // Errnos not in this list (EAGAIN, EINTR, EACCES, ...) pass through // unchanged and are classified as Platform_Error. normalize_to_zmq :: proc(raw: c.int) -> c.int { #partial switch os.Platform_Error(raw) { // ENOTSUP shares its value with EOPNOTSUPP on Linux/macOS (both are // the same constant). On Windows they differ (129 vs 130), but MSVC- // compiled libzmq uses the ZMQ fallback (HAUSNUMERO+1) anyway, so // .EOPNOTSUPP here only needs to match the Linux/macOS value. case .EOPNOTSUPP: return HAUSNUMERO + 1 case .EPROTONOSUPPORT: return HAUSNUMERO + 2 case .ENOBUFS: return HAUSNUMERO + 3 case .ENETDOWN: return HAUSNUMERO + 4 case .EADDRINUSE: return HAUSNUMERO + 5 case .EADDRNOTAVAIL: return HAUSNUMERO + 6 case .ECONNREFUSED: return HAUSNUMERO + 7 case .EINPROGRESS: return HAUSNUMERO + 8 case .ENOTSOCK: return HAUSNUMERO + 9 case .EMSGSIZE: return HAUSNUMERO + 10 case .EAFNOSUPPORT: return HAUSNUMERO + 11 case .ENETUNREACH: return HAUSNUMERO + 12 case .ECONNABORTED: return HAUSNUMERO + 13 case .ECONNRESET: return HAUSNUMERO + 14 case .ENOTCONN: return HAUSNUMERO + 15 case .ETIMEDOUT: return HAUSNUMERO + 16 case .EHOSTUNREACH: return HAUSNUMERO + 17 case .ENETRESET: return HAUSNUMERO + 18 case: return raw } } // Reverse of normalize_to_zmq: maps ZMQ fallback values back to // platform-native errno values. // // zmq_strerror only has string entries for ZMQ-native codes (HAUSNUMERO+51..54) // and delegates everything else to C's strerror, which only knows platform-native // errno values. The ZMQ fallback values (HAUSNUMERO+1..18) are in a gap — neither // ZMQ-native nor platform-native — so zmq_strerror returns "Unknown error" for them. // This function reverses the normalization so strerror produces the correct string. denormalize_from_zmq :: proc(zmq_val: c.int) -> c.int { #partial switch ZMQ_Error(zmq_val) { case .ENOTSUP: return c.int(os.Platform_Error.EOPNOTSUPP) case .EPROTONOSUPPORT: return c.int(os.Platform_Error.EPROTONOSUPPORT) case .ENOBUFS: return c.int(os.Platform_Error.ENOBUFS) case .ENETDOWN: return c.int(os.Platform_Error.ENETDOWN) case .EADDRINUSE: return c.int(os.Platform_Error.EADDRINUSE) case .EADDRNOTAVAIL: return c.int(os.Platform_Error.EADDRNOTAVAIL) case .ECONNREFUSED: return c.int(os.Platform_Error.ECONNREFUSED) case .EINPROGRESS: return c.int(os.Platform_Error.EINPROGRESS) case .ENOTSOCK: return c.int(os.Platform_Error.ENOTSOCK) case .EMSGSIZE: return c.int(os.Platform_Error.EMSGSIZE) case .EAFNOSUPPORT: return c.int(os.Platform_Error.EAFNOSUPPORT) case .ENETUNREACH: return c.int(os.Platform_Error.ENETUNREACH) case .ECONNABORTED: return c.int(os.Platform_Error.ECONNABORTED) case .ECONNRESET: return c.int(os.Platform_Error.ECONNRESET) case .ENOTCONN: return c.int(os.Platform_Error.ENOTCONN) case .ETIMEDOUT: return c.int(os.Platform_Error.ETIMEDOUT) case .EHOSTUNREACH: return c.int(os.Platform_Error.EHOSTUNREACH) case .ENETRESET: return c.int(os.Platform_Error.ENETRESET) case: return zmq_val } } last_error :: proc() -> Error { raw := errno() normalized := normalize_to_zmq(raw) #partial switch ZMQ_Error(normalized) { case .ENOTSUP, .EPROTONOSUPPORT, .ENOBUFS, .ENETDOWN, .EADDRINUSE, .EADDRNOTAVAIL, .ECONNREFUSED, .EINPROGRESS, .ENOTSOCK, .EMSGSIZE, .EAFNOSUPPORT, .ENETUNREACH, .ECONNABORTED, .ECONNRESET, .ENOTCONN, .ETIMEDOUT, .EHOSTUNREACH, .ENETRESET, .EFSM, .ENOCOMPATPROTO, .ETERM, .EMTHREAD: return ZMQ_Error(normalized) case: return os.Platform_Error(raw) } }