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}