//////////////////////////////// //~ Channel ID Struct(N_ChannelId) { u32 gen; u32 idx; }; //////////////////////////////// //~ Host command types Enum(N_CmdKind) { N_CmdKind_None, N_CmdKind_TryConnect, N_CmdKind_ConnectSuccess, N_CmdKind_Disconnect, N_CmdKind_Heartbeat, N_CmdKind_Write }; Enum(N_WriteFlag) { N_WriteFlag_None = 0, N_WriteFlag_Reliable = (1 << 0) }; Struct(N_Cmd) { N_CmdKind kind; N_ChannelId channel_id; u16 heartbeat_id; u16 heartbeat_ack_id; b32 write_reliable; String write_msg; N_Cmd *next; }; //////////////////////////////// //~ Event types Enum(N_EventKind) { N_EventKind_None, N_EventKind_ChannelOpened, N_EventKind_ChannelClosed, N_EventKind_Msg }; Struct(N_Event) { N_EventKind kind; N_ChannelId channel_id; String msg; N_Event *next; }; Struct(N_EventList) { N_Event *first; N_Event *last; }; Struct(N_ChannelLookupBin) { struct N_Channel *first; struct N_Channel *last; }; //////////////////////////////// //~ Packet types #define N_PacketMagic 0xd9e3b8b6 #define N_MaxPacketChunkLen 1024 #define N_MaxPacketLen 1280 /* Give enough space for msg chunk + header */ Enum(N_PacketKind) { N_PacketKind_None, N_PacketKind_TryConnect, N_PacketKind_ConnectSuccess, N_PacketKind_Disconnect, N_PacketKind_Heartbeat, N_PacketKind_MsgChunk }; Enum(N_PacketFlag) { N_PacketFlag_None = 0, N_PacketFlag_Reliable = (1 << 0) }; Struct(N_SndPacket) { N_SndPacket *next; u64 seq; u64 data_len; u8 data[N_MaxPacketLen]; }; Struct(N_RcvPacket) { P_Sock *sock; P_Address address; String data; N_RcvPacket *next; }; Struct(N_RcvBuffer) { Arena *arena; N_RcvPacket *first_packet; N_RcvPacket *last_packet; }; //////////////////////////////// //~ Channel types Struct(N_Channel) { N_ChannelId id; b32 valid; b32 connected; struct N_Host *host; N_Channel *next_free; P_Address address; u64 address_hash; N_Channel *next_address_hash; N_Channel *prev_address_hash; /* NOTE: Packets are allocated in host's `arena` */ N_SndPacket *first_reliable_packet; N_SndPacket *last_reliable_packet; N_SndPacket *first_unreliable_packet; N_SndPacket *last_unreliable_packet; u64 num_reliable_packets; u64 num_unreliable_packets; /* NOTE: Msg assemblers are allocated in host's `arena` */ struct N_MsgAssembler *least_recent_msg_assembler; struct N_MsgAssembler *most_recent_msg_assembler; u16 last_heartbeat_received_id; u16 last_heartbeat_acked_id; i64 last_heartbeat_acked_ns; i64 last_heartbeat_rtt_ns; u64 last_sent_msg_id; u64 their_acked_seq; u64 our_acked_seq; u64 last_sent_seq; i64 last_packet_received_ns; }; Struct(N_ChannelNode) { N_Channel *channel; N_ChannelNode *next; }; Struct(N_ChannelList) { N_ChannelNode *first; N_ChannelNode *last; }; //////////////////////////////// //~ Message asssembler types Struct(N_MsgAssembler) { N_Channel *channel; b32 is_reliable; /* Free list */ N_MsgAssembler *next_free; /* Bucket list */ N_MsgAssembler *next_hash; N_MsgAssembler *prev_hash; /* Channel list */ N_MsgAssembler *less_recent; N_MsgAssembler *more_recent; u64 msg_id; u64 hash; u64 last_chunk_len; u64 num_chunks_total; u64 num_chunks_received; i64 touched_ns; BuddyBlock *buddy_block; u8 *chunk_bitmap; u8 *chunk_data; }; Struct(N_MsgAssemblerLookupBin) { N_MsgAssembler *first; N_MsgAssembler *last; }; //////////////////////////////// //~ Host types #define N_NumChannelLookupBins 512 #define N_NumMsgAssemblerLookupBins 16384 Struct(N_Host) { Arena *arena; P_Sock *sock; BuddyCtx *buddy; /* For storing msg assembler data */ Arena *cmd_arena; N_Cmd *first_cmd; N_Cmd *last_cmd; N_Cmd *first_free_cmd; Arena *channel_arena; N_Channel *channels; N_Channel *first_free_channel; u64 num_channels_reserved; N_SndPacket *first_free_packet; /* Acquired in `arena` */ N_MsgAssembler *first_free_msg_assembler; /* Acquired in `arena` */ N_ChannelLookupBin *channel_lookup_bins; /* Acquired in `arena` */ u64 num_channel_lookup_bins; N_MsgAssemblerLookupBin *msg_assembler_lookup_bins; /* Acquired in `arena` */ u64 num_msg_assembler_lookup_bins; /* Double buffer for incoming data */ Mutex rcv_buffer_write_mutex; N_RcvBuffer *rcv_buffer_read; N_RcvBuffer *rcv_buffer_write; u64 bytes_received; u64 bytes_sent; }; //////////////////////////////// //~ Nil constants Readonly Global N_Channel N_nil_channel = { .valid = 0 }; //////////////////////////////// //~ Host initialization N_Host *N_AcquireHost(u16 listen_port); void N_ReleaseHost(N_Host *host); //////////////////////////////// //~ Channel operations #define N_NilChannelId (N_ChannelId) { .gen = 0, .idx = 0 } #define N_AllChannelsId (N_ChannelId) { .gen = U32Max, .idx = U32Max } #define N_EqChannelId(a, b) ((a).gen == (b).gen && (a).idx == (b).idx) #define N_IsChannelIdNil(v) ((v).gen == 0 && (v).idx == 0) u64 N_HashFromAddress(P_Address address); N_Channel *N_ChannelFromAddress(N_Host *host, P_Address address); N_Channel *N_ChannelFromId(N_Host *host, N_ChannelId channel_id); N_ChannelList N_ChannelsFromId(Arena *arena, N_Host *host, N_ChannelId channel_id); N_Channel *N_AcquireChannel(N_Host *host, P_Address address); void N_ReleaseChannel(N_Channel *channel); //////////////////////////////// //~ Message assembler operations u64 N_HashFromMsg(N_ChannelId channel_id, u64 msg_id); N_MsgAssembler *N_MsgAssemblerFromMsg(N_Host *host, N_ChannelId channel_id, u64 msg_id); N_MsgAssembler *N_AcquireMsgAssembler(N_Channel *channel, u64 msg_id, u64 chunk_count, u64 now_ns, b32 is_reliable); void N_ReleaseMessageAssembler(N_MsgAssembler *ma); void N_TouchMessageAssembler(N_MsgAssembler *ma, i64 now_ns); b32 N_IsChunkFilled(N_MsgAssembler *ma, u64 chunk_id); void N_MarkChunkReceived(N_MsgAssembler *ma, u64 chunk_id); //////////////////////////////// //~ Packet operations N_SndPacket *N_PushSndPacket(N_Channel *channel, b32 is_reliable); //////////////////////////////// //~ Host command operations N_Cmd *N_PushCmd(N_Host *host); void N_Connect(N_Host *host, P_Address connect_address); void N_Disconnect(N_Host *host, N_ChannelId channel_id); void N_Write(N_Host *host, N_ChannelId channel_id, String msg, N_WriteFlag flags); //////////////////////////////// //~ Channel info operations i64 N_GetChannelLastRttNs(N_Host *host, N_ChannelId channel_id); //////////////////////////////// //~ Update N_Event *N_PushEvent(Arena *arena, N_EventList *list); N_EventList N_BeginUpdate(Arena *arena, N_Host *host); void N_EndUpdate(N_Host *host);