Skip to main content

ri/c/
protocol.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of Ri.
4//! The Ri project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18//! # Protocol Module C API
19//!
20//! This module provides C language bindings for Ri's protocol handling infrastructure. The protocol
21//! module delivers comprehensive support for encoding, decoding, and transforming data across various
22//! wire formats and communication protocols. This C API enables C/C++ applications to leverage Ri's
23//! protocol capabilities for building interoperable distributed systems with standardized data exchange.
24//!
25//! ## Module Architecture
26//!
27//! The protocol module comprises three primary components that together provide complete protocol
28//! management capabilities:
29//!
30//! - **RiProtocolConfig**: Configuration container for protocol codec parameters including encoding
31//!   formats, framing options, compression settings, and validation rules. The configuration object
32//!   controls how data is serialized and deserialized, ensuring consistent behavior across the
33//!   application.
34//!
35//! - **RiProtocolManager**: Central manager for protocol registration, codec lookup, and protocol
36//!   negotiation. The manager handles the complete lifecycle of protocol operations including codec
37//!   selection, error handling, and protocol switching.
38//!
39//! - **RiFrame**: Low-level frame abstraction for message framing and boundary management. Frames
40//!   provide the foundation for streaming protocols, handling message boundaries, chunking, and
41//!   reassembly.
42//!
43//! ## Supported Protocols
44//!
45//! The protocol system supports a comprehensive range of wire formats:
46//!
47//! - **JSON (JavaScript Object Notation)**: Human-readable data interchange format widely used in
48//!   web APIs and microservices. Supports schema validation and transformation.
49//!
50//! - **MessagePack**: Binary serialization format providing compact representation with fast
51//!   encoding and decoding. Ideal for bandwidth-constrained environments.
52//!
53//! - **Protocol Buffers**: Google's language-neutral, platform-neutral, extensible mechanism for
54//!   serializing structured data. Provides strong typing and backward/forward compatibility.
55//!
56//! - **CBOR (Concise Binary Object Representation)**: Binary JSON-like format designed for small
57//!   code size and small message size. IETF standard (RFC 8949).
58//!
59//! - **BSON (Binary JSON)**: MongoDB's binary-encoded JSON format with additional data types
60//!   like dates and binary blobs.
61//!
62//! - **Avro**: Apache Avro data serialization format with schema evolution support and
63//!   compact binary encoding.
64//!
65//! ## Framing Protocols
66//!
67//! The module provides various message framing approaches:
68//!
69//! - **Length-Prefixed Framing**: Each message is prefixed with its length in bytes. Enables
70//!   streaming parsing and message boundary detection without special delimiters.
71//!
72//! - **Delimiter-Based Framing**: Messages are separated by special delimiter bytes (e.g., newline
73//!   for line-based protocols). Simple but requires escaping for binary data.
74//!
75//! - **Fixed-Size Framing**: All messages have identical length. Simplifies parsing but wastes
76//!   bandwidth for variable-sized data.
77//!
78//! - **HTTP/1.1 Chunked Transfer**: Standard HTTP chunked encoding for streaming responses.
79//!   Supports incremental processing of large payloads.
80//!
81//! - **WebSocket Framing**: Full WebSocket frame handling including control frames, continuation
82//!   frames, and fragmentation support.
83//!
84//! ## Compression
85//!
86//! Built-in compression support reduces bandwidth usage:
87//!
88//! - **Gzip**: GNU zip compression with wide compatibility. Good balance of compression ratio
89//!   and CPU usage.
90//!
91//! - **Snappy**: Google's compression library designed for high speeds. Lower compression
92//!   ratio but very fast encoding and decoding.
93//!
94//! - **LZ4**: Extremely fast compression with reasonable ratios. Ideal for real-time systems
95//!   with limited CPU budget.
96//!
97//! - **Zstandard (zstd)**: Facebook's compression algorithm offering excellent compression ratios
98//!   at high speeds. Supports dictionary compression for repetitive data.
99//!
100//! - **Brotli**: Google's next-generation compression with best-in-class compression ratios.
101//!   Slightly slower but excellent for static content delivery.
102//!
103//! ## Validation
104//!
105//! Comprehensive data validation ensures protocol integrity:
106//!
107//! - **Schema Validation**: Validate messages against predefined schemas before processing.
108//!   Catches malformed or unexpected data early.
109//!
110//! - **Type Checking**: Verify message types match expected types for each field.
111//!   Supports optional fields and type coercion.
112//!
113//! - **Range Validation**: Ensure numeric values fall within acceptable ranges.
114//!   Prevents overflow and underflow issues.
115//!
116//! - **Pattern Matching**: Validate string fields against regex patterns or format strings.
117//!   Ensures email, UUID, and other formatted data validity.
118//!
119//! - **Custom Validators**: User-defined validation functions for domain-specific rules.
120//!   Extend built-in validation with application logic.
121//!
122//! ## Serialization Features
123//!
124//! Advanced serialization capabilities:
125//!
126//! - **Polymorphism**: Handle tagged unions and inheritance hierarchies through type tags.
127//!   Enables message routing based on message type.
128//!
129//! - **Optional Fields**: Gracefully handle missing fields with optional type support.
130//!   Backward compatibility maintained across schema versions.
131//!
132//! - **Default Values**: Automatic default values for missing fields when defined in schema.
133//!   Simplifies client code with sensible fallbacks.
134//!
135//! - **Unknown Field Handling**: Option to preserve unknown fields during deserialization.
136//!   Enables forward compatibility without data loss.
137//!
138//! - **Circular Reference Handling**: Detect and properly serialize graph structures with
139//!   references between objects.
140//!
141//! ## Performance Characteristics
142//!
143//! Protocol operations are optimized for various use cases:
144//!
145//! - **JSON Parsing**: O(n) where n is message size, optimized with SIMD instructions
146//! - **Binary Codecs**: O(n) with low constant factors, ideal for high-throughput scenarios
147//! - **Framing**: O(1) per frame boundary detection
148//! - **Compression**: O(n * compression_level), configurable trade-off
149//! - **Validation**: O(n) with early termination on first error
150//!
151//! ## Memory Management
152//!
153//! All C API objects use opaque pointers with manual memory management:
154//!
155//! - Constructor functions allocate new instances on the heap
156//! - Destructor functions must be called to release memory
157//! - Codec instances are managed by the protocol manager
158//! - Frame buffers are recycled for performance
159//!
160//! ## Thread Safety
161//!
162//! The underlying implementations are thread-safe:
163//!
164//! - Protocol manager supports concurrent codec registration
165//! - Codec instances are immutable after creation
166//! - Frame allocation uses thread-local pools
167//! - Validation can be performed concurrently
168//!
169//! ## Usage Example
170//!
171//! ```c
172//! // Create protocol configuration
173//! RiProtocolConfig* config = ri_protocol_config_new();
174//! if (config == NULL) {
175//!     fprintf(stderr, "Failed to create protocol config\n");
176//!     return ERROR_INIT;
177//! }
178//!
179//! // Configure protocol settings
180//! ri_protocol_config_set_format(config, PROTOCOL_FORMAT_MSGPACK);
181//! ri_protocol_config_set_compression(config, COMPRESSION_SNAPPY);
182//! ri_protocol_config_set_validation_enabled(config, true);
183//!
184//! // Create protocol manager
185//! RiProtocolManager* manager = ri_protocol_manager_new(config);
186//! if (manager == NULL) {
187//!     fprintf(stderr, "Failed to create protocol manager\n");
188//!     ri_protocol_config_free(config);
189//!     return ERROR_INIT;
190//! }
191//!
192//! // Register custom schema
193//! int result = ri_protocol_manager_register_schema(
194//!     manager,
195//!     "UserMessage",
196//!     user_schema_definition,
197//!     sizeof(user_schema_definition)
198//! );
199//!
200//! if (result != 0) {
201//!     fprintf(stderr, "Failed to register schema\n");
202//! }
203//!
204//! // Create frame for streaming
205//! RiFrame* frame = ri_frame_new();
206//! if (frame == NULL) {
207//!     fprintf(stderr, "Failed to create frame\n");
208//!     ri_protocol_manager_free(manager);
209//!     ri_protocol_config_free(config);
210//!     return ERROR_INIT;
211//! }
212//!
213//! // Encode message
214//! const char* input_data = "{\"user_id\": 12345, \"name\": \"John\"}";
215//! size_t input_len = strlen(input_data);
216//!
217//! char* output_buffer = NULL;
218//! size_t output_len = 0;
219//!
220//! result = ri_protocol_manager_encode(
221//!     manager,
222//!     "UserMessage",
223//!     input_data,
224//!     input_len,
225//!     &output_buffer,
226//!     &output_len
227//! );
228//!
229//! if (result == 0 && output_buffer != NULL) {
230//!     printf("Encoded %zu bytes\n", output_len);
231//!
232//!     // Decode message back
233//!     char* decoded_buffer = NULL;
234//!     size_t decoded_len = 0;
235//!
236//!         int decode_result = ri_protocol_manager_decode(
237//!             manager,
238//!             "UserMessage",
239//!             output_buffer,
240//!             output_len,
241//!             &decoded_buffer,
242//!             &decoded_len
243//!         );
244//!
245//!         if (decode_result == 0) {
246//!             printf("Decoded: %.*s\n", (int)decoded_len, decoded_buffer);
247//!             ri_string_free(decoded_buffer);
248//!         }
249//!
250//!     ri_string_free(output_buffer);
251//! }
252//!
253//! // Frame the message for transport
254//! ri_frame_reset(frame);
255//! ri_frame_append(frame, output_buffer, output_len);
256//!
257//! // Read framed data
258//! const char* frame_data = ri_frame_data(frame);
259//! size_t frame_size = ri_frame_size(frame);
260//!
261//! // Cleanup
262//! ri_frame_free(frame);
263//! ri_protocol_manager_free(manager);
264//! ri_protocol_config_free(config);
265//! ```
266//!
267//! ## Protocol Negotiation
268//!
269//! The protocol manager supports dynamic protocol negotiation:
270//!
271//! - **Capability Exchange**: During connection establishment, both ends advertise supported
272//!   protocols and versions.
273//!
274//! - **Common Protocol Selection**: Automatically select the best mutually-supported protocol
275//!   based on priority and capabilities.
276//!
277//! - **Protocol Upgrades**: Support for upgrading from a base protocol (like HTTP/1.1) to
278//!   a more efficient protocol (like WebSocket or gRPC).
279//!
280//! - **Version Handling**: Manage multiple protocol versions simultaneously for backward
281//!   compatibility during migrations.
282//!
283//! ## Dependencies
284//!
285//! This module depends on the following Ri components:
286//!
287//! - `crate::protocol`: Rust protocol module implementation
288//! - `crate::prelude`: Common types and traits
289//! - serde for serialization frameworks
290//! - Various codec libraries (serde_json, rmp-serde, prost, etc.)
291//!
292//! ## Feature Flags
293//!
294//! The protocol module is enabled by default with comprehensive format support.
295//! Additional formats enabled by feature flags:
296//!
297//! - `protocol-protobuf`: Enable Protocol Buffer support (requires prost)
298//! - `protocol-avro`: Enable Apache Avro support
299//! - `protocol-cbor`: Enable CBOR support
300//! - `protocol-bson`: Enable BSON support
301//! - `protocol-compression`: Enable compression codecs
302
303use crate::protocol::{RiFrame, RiProtocolConfig, RiProtocolManager, RiProtocolStats, RiConnectionInfo, RiProtocolType, RiSecurityLevel, RiConnectionState};
304
305
306c_wrapper!(CRiProtocolConfig, RiProtocolConfig);
307c_wrapper!(CRiProtocolManager, RiProtocolManager);
308c_wrapper!(CRiFrame, RiFrame);
309c_wrapper!(CRiProtocolStats, RiProtocolStats);
310c_wrapper!(CRiConnectionInfo, RiConnectionInfo);
311
312// RiProtocolConfig constructors and destructors
313c_constructor!(
314    ri_protocol_config_new,
315    CRiProtocolConfig,
316    RiProtocolConfig,
317    RiProtocolConfig::default()
318);
319c_destructor!(ri_protocol_config_free, CRiProtocolConfig);
320
321// RiProtocolConfig setters
322#[no_mangle]
323pub extern "C" fn ri_protocol_config_set_protocol_type(config: *mut CRiProtocolConfig, protocol_type: std::ffi::c_int) -> std::ffi::c_int {
324    if config.is_null() {
325        return -1;
326    }
327    unsafe {
328        let pt = match protocol_type {
329            0 => RiProtocolType::Global,
330            1 => RiProtocolType::Private,
331            _ => RiProtocolType::Global,
332        };
333        (*config).inner.default_protocol = pt;
334    }
335    0
336}
337
338#[no_mangle]
339pub extern "C" fn ri_protocol_config_set_security_enabled(config: *mut CRiProtocolConfig, enabled: bool) -> std::ffi::c_int {
340    if config.is_null() {
341        return -1;
342    }
343    unsafe {
344        (*config).inner.enable_security = enabled;
345    }
346    0
347}
348
349#[no_mangle]
350pub extern "C" fn ri_protocol_config_set_security_level(config: *mut CRiProtocolConfig, level: std::ffi::c_int) -> std::ffi::c_int {
351    if config.is_null() {
352        return -1;
353    }
354    unsafe {
355        let sl = match level {
356            0 => RiSecurityLevel::None,
357            1 => RiSecurityLevel::Standard,
358            2 => RiSecurityLevel::High,
359            3 => RiSecurityLevel::Military,
360            _ => RiSecurityLevel::Standard,
361        };
362        (*config).inner.security_level = sl;
363    }
364    0
365}
366
367// RiProtocolManager C bindings
368#[no_mangle]
369pub extern "C" fn ri_protocol_manager_new() -> *mut CRiProtocolManager {
370    Box::into_raw(Box::new(CRiProtocolManager::new(RiProtocolManager::new())))
371}
372c_destructor!(ri_protocol_manager_free, CRiProtocolManager);
373
374#[no_mangle]
375pub extern "C" fn ri_protocol_manager_send(
376    manager: *mut CRiProtocolManager,
377    target: *const std::ffi::c_char,
378    data: *const std::ffi::c_char,
379    data_len: usize,
380    out_response: *mut *mut std::ffi::c_char,
381    out_len: *mut usize,
382) -> std::ffi::c_int {
383    if manager.is_null() || target.is_null() || data.is_null() || out_response.is_null() || out_len.is_null() {
384        return -1;
385    }
386    let target_str = match unsafe { std::ffi::CStr::from_ptr(target).to_str() } {
387        Ok(s) => s,
388        Err(_) => return -2,
389    };
390    let data_slice = unsafe { std::slice::from_raw_parts(data as *const u8, data_len) };
391    let response = unsafe { (*manager).inner.send_message(target_str, data_slice) };
392    unsafe { *out_len = response.len(); }
393    match std::ffi::CString::new(response) {
394        Ok(c_str) => {
395            unsafe { *out_response = c_str.into_raw(); }
396            0
397        }
398        Err(_) => -3,
399    }
400}
401
402#[no_mangle]
403pub extern "C" fn ri_protocol_manager_get_stats(
404    manager: *mut CRiProtocolManager,
405    out_messages_sent: *mut u64,
406    out_messages_received: *mut u64,
407    out_bytes_sent: *mut u64,
408    out_bytes_received: *mut u64,
409    out_errors: *mut u64,
410) -> std::ffi::c_int {
411    if manager.is_null() {
412        return -1;
413    }
414    let rt = match tokio::runtime::Runtime::new() {
415        Ok(rt) => rt,
416        Err(_) => return -2,
417    };
418    unsafe {
419        let stats = rt.block_on(async { (*manager).inner.stats.read().await.clone() });
420        if !out_messages_sent.is_null() {
421            *out_messages_sent = stats.messages_sent;
422        }
423        if !out_messages_received.is_null() {
424            *out_messages_received = stats.messages_received;
425        }
426        if !out_bytes_sent.is_null() {
427            *out_bytes_sent = stats.bytes_sent;
428        }
429        if !out_bytes_received.is_null() {
430            *out_bytes_received = stats.bytes_received;
431        }
432        if !out_errors.is_null() {
433            *out_errors = stats.errors;
434        }
435        0
436    }
437}
438
439#[no_mangle]
440pub extern "C" fn ri_protocol_manager_get_connection_count(manager: *mut CRiProtocolManager) -> usize {
441    if manager.is_null() {
442        return 0;
443    }
444    unsafe { (*manager).inner.get_connection_count() }
445}
446
447// RiFrame C bindings
448#[no_mangle]
449pub extern "C" fn ri_frame_new() -> *mut CRiFrame {
450    Box::into_raw(Box::new(CRiFrame::new(RiFrame::default())))
451}
452c_destructor!(ri_frame_free, CRiFrame);
453
454#[no_mangle]
455pub extern "C" fn ri_frame_get_payload_size(frame: *mut CRiFrame) -> usize {
456    if frame.is_null() {
457        return 0;
458    }
459    unsafe { (*frame).inner.payload.len() }
460}
461
462#[no_mangle]
463pub extern "C" fn ri_frame_get_payload(frame: *mut CRiFrame, out_data: *mut *mut std::ffi::c_char, out_len: *mut usize) -> std::ffi::c_int {
464    if frame.is_null() || out_data.is_null() || out_len.is_null() {
465        return -1;
466    }
467    unsafe {
468        let payload = (*frame).inner.payload.clone();
469        *out_len = payload.len();
470        let ptr = Box::into_raw(payload.into_boxed_slice()) as *mut std::ffi::c_char;
471        *out_data = ptr;
472        0
473    }
474}
475
476#[no_mangle]
477pub extern "C" fn ri_frame_get_sequence(frame: *mut CRiFrame) -> u64 {
478    if frame.is_null() {
479        return 0;
480    }
481    unsafe { (*frame).inner.header.sequence_number }
482}
483
484#[no_mangle]
485pub extern "C" fn ri_frame_get_timestamp(frame: *mut CRiFrame) -> u64 {
486    if frame.is_null() {
487        return 0;
488    }
489    unsafe { (*frame).inner.header.timestamp }
490}
491
492#[no_mangle]
493pub extern "C" fn ri_frame_get_type(frame: *mut CRiFrame) -> std::ffi::c_int {
494    if frame.is_null() {
495        return -1;
496    }
497    unsafe {
498        match (*frame).inner.header.frame_type {
499            crate::protocol::RiFrameType::Data => 0,
500            crate::protocol::RiFrameType::Control => 1,
501            crate::protocol::RiFrameType::Heartbeat => 2,
502            crate::protocol::RiFrameType::Ack => 3,
503            crate::protocol::RiFrameType::Error => 4,
504        }
505    }
506}
507
508c_string_getter!(
509    ri_frame_get_source_id,
510    CRiFrame,
511    |inner: &RiFrame| inner.source_id.clone()
512);
513
514c_string_getter!(
515    ri_frame_get_target_id,
516    CRiFrame,
517    |inner: &RiFrame| inner.target_id.clone()
518);
519
520// RiConnectionInfo C bindings
521c_destructor!(ri_connection_info_free, CRiConnectionInfo);
522
523c_string_getter!(
524    ri_connection_info_get_id,
525    CRiConnectionInfo,
526    |inner: &RiConnectionInfo| inner.connection_id.clone()
527);
528
529c_string_getter!(
530    ri_connection_info_get_device_id,
531    CRiConnectionInfo,
532    |inner: &RiConnectionInfo| inner.device_id.clone()
533);
534
535c_string_getter!(
536    ri_connection_info_get_address,
537    CRiConnectionInfo,
538    |inner: &RiConnectionInfo| inner.address.clone()
539);
540
541#[no_mangle]
542pub extern "C" fn ri_connection_info_get_state(info: *mut CRiConnectionInfo) -> std::ffi::c_int {
543    if info.is_null() {
544        return -1;
545    }
546    unsafe {
547        match (*info).inner.state {
548            RiConnectionState::Disconnected => 0,
549            RiConnectionState::Connecting => 1,
550            RiConnectionState::Connected => 2,
551            RiConnectionState::Disconnecting => 3,
552        }
553    }
554}
555
556#[no_mangle]
557pub extern "C" fn ri_connection_info_get_security_level(info: *mut CRiConnectionInfo) -> std::ffi::c_int {
558    if info.is_null() {
559        return -1;
560    }
561    unsafe {
562        match (*info).inner.security_level {
563            RiSecurityLevel::None => 0,
564            RiSecurityLevel::Standard => 1,
565            RiSecurityLevel::High => 2,
566            RiSecurityLevel::Military => 3,
567        }
568    }
569}
570
571#[no_mangle]
572pub extern "C" fn ri_connection_info_get_protocol_type(info: *mut CRiConnectionInfo) -> std::ffi::c_int {
573    if info.is_null() {
574        return -1;
575    }
576    unsafe {
577        match (*info).inner.protocol_type {
578            RiProtocolType::Global => 0,
579            RiProtocolType::Private => 1,
580        }
581    }
582}
583
584// RiProtocolStats C bindings
585c_destructor!(ri_protocol_stats_free, CRiProtocolStats);
586
587#[no_mangle]
588pub extern "C" fn ri_protocol_stats_get_messages_sent(stats: *mut CRiProtocolStats) -> u64 {
589    if stats.is_null() {
590        return 0;
591    }
592    unsafe { (*stats).inner.messages_sent }
593}
594
595#[no_mangle]
596pub extern "C" fn ri_protocol_stats_get_messages_received(stats: *mut CRiProtocolStats) -> u64 {
597    if stats.is_null() {
598        return 0;
599    }
600    unsafe { (*stats).inner.messages_received }
601}
602
603#[no_mangle]
604pub extern "C" fn ri_protocol_stats_get_bytes_sent(stats: *mut CRiProtocolStats) -> u64 {
605    if stats.is_null() {
606        return 0;
607    }
608    unsafe { (*stats).inner.bytes_sent }
609}
610
611#[no_mangle]
612pub extern "C" fn ri_protocol_stats_get_bytes_received(stats: *mut CRiProtocolStats) -> u64 {
613    if stats.is_null() {
614        return 0;
615    }
616    unsafe { (*stats).inner.bytes_received }
617}
618
619#[no_mangle]
620pub extern "C" fn ri_protocol_stats_get_errors(stats: *mut CRiProtocolStats) -> u64 {
621    if stats.is_null() {
622        return 0;
623    }
624    unsafe { (*stats).inner.errors }
625}
626
627#[no_mangle]
628pub extern "C" fn ri_protocol_stats_get_avg_latency_ms(stats: *mut CRiProtocolStats) -> f64 {
629    if stats.is_null() {
630        return 0.0;
631    }
632    unsafe { (*stats).inner.avg_latency_ms }
633}