1
+ using System ;
2
+ using Confluent . Kafka . Impl ;
3
+ using Confluent . Kafka . Internal ;
4
+
5
+ namespace Confluent . Kafka ;
6
+
7
+ /// <summary>
8
+ /// Experimental alloc-free APIs.
9
+ /// Note that these APIs can be changed substantially or removed entirely in the future versions.
10
+ /// </summary>
11
+ public abstract class Experimental
12
+ {
13
+ /// <summary>
14
+ /// Callback delegate for allocation-free message consumption.
15
+ /// </summary>
16
+ /// <param name="reader">Message reader providing allocation-free access to message data</param>
17
+ public delegate void AllocFreeConsumeCallback ( in MessageReader reader ) ;
18
+
19
+ /// <summary>
20
+ /// Alloc-free delivery handler
21
+ /// </summary>
22
+ public delegate void AllocFreeDeliveryHandler ( in MessageReader arg ) ;
23
+
24
+ /// <summary>
25
+ /// Allocation-free message reader that provides access to consumed message data.
26
+ /// This is a ref struct to ensure stack allocation and prevent escaping the callback scope.
27
+ /// </summary>
28
+ public unsafe ref struct MessageReader
29
+ {
30
+ private readonly rd_kafka_message * msg ;
31
+ private IntPtr hdrsPtr = IntPtr . Zero ;
32
+
33
+ internal MessageReader (
34
+ rd_kafka_message * msg )
35
+ {
36
+ this . msg = msg ;
37
+ }
38
+
39
+ /// <summary>Gets the topic name</summary>
40
+ public string Topic =>
41
+ msg ->rkt != IntPtr . Zero
42
+ ? Util . Marshal . PtrToStringUTF8 ( Librdkafka . topic_name ( msg ->rkt ) )
43
+ : null ;
44
+
45
+ /// <summary>Gets the partition</summary>
46
+ public Partition Partition => msg ->partition ;
47
+
48
+ /// <summary>Gets the error code</summary>
49
+ public ErrorCode ErrorCode => msg ->err ;
50
+
51
+ /// <summary>Gets the offset</summary>
52
+ public Offset Offset => msg ->offset ;
53
+
54
+ /// <summary>Gets the leader epoch</summary>
55
+ public int ? LeaderEpoch =>
56
+ msg ->rkt != IntPtr . Zero && msg ->offset != Offset . Unset
57
+ ? Librdkafka . message_leader_epoch ( ( IntPtr ) msg )
58
+ : null ;
59
+
60
+ /// <summary>Gets the timestamp</summary>
61
+ public Timestamp Timestamp
62
+ {
63
+ get
64
+ {
65
+ var timestampUnix = Librdkafka . message_timestamp ( ( IntPtr ) msg , out var timestampType ) ;
66
+ return new Timestamp ( timestampUnix , ( TimestampType ) timestampType ) ;
67
+ }
68
+ }
69
+
70
+ /// <summary>Gets whether this indicates partition EOF</summary>
71
+ public bool IsPartitionEOF => msg ->err == ErrorCode . Local_PartitionEOF ;
72
+
73
+ /// <summary>
74
+ /// Gets the raw key data as a ReadOnlySpan without allocation
75
+ /// </summary>
76
+ public ReadOnlySpan < byte > KeySpan =>
77
+ msg ->key == IntPtr . Zero
78
+ ? ReadOnlySpan < byte > . Empty
79
+ : new ReadOnlySpan < byte > ( msg ->key . ToPointer ( ) , ( int ) msg ->key_len ) ;
80
+
81
+ /// <summary>
82
+ /// Gets the raw value data as a ReadOnlySpan without allocation
83
+ /// </summary>
84
+ public ReadOnlySpan < byte > ValueSpan =>
85
+ msg ->val == IntPtr . Zero
86
+ ? ReadOnlySpan < byte > . Empty
87
+ : new ReadOnlySpan < byte > ( msg ->val . ToPointer ( ) , ( int ) msg ->len ) ;
88
+
89
+ /// <summary>
90
+ /// Gets a header by index without allocation
91
+ /// </summary>
92
+ /// <param name="index">Zero-based header index</param>
93
+ /// <param name="name">Header name (allocated string)</param>
94
+ /// <param name="value">Header value as ReadOnlySpan</param>
95
+ /// <returns>True if header exists at the given index</returns>
96
+ public bool TryGetHeader ( int index , out string name , out ReadOnlySpan < byte > value )
97
+ {
98
+ name = null ;
99
+ value = ReadOnlySpan < byte > . Empty ;
100
+
101
+ if ( hdrsPtr == IntPtr . Zero )
102
+ {
103
+ Librdkafka . message_headers ( ( IntPtr ) msg , out hdrsPtr ) ;
104
+ if ( hdrsPtr == IntPtr . Zero )
105
+ return false ;
106
+ }
107
+
108
+ var err = Librdkafka . header_get_all (
109
+ hdrsPtr ,
110
+ ( IntPtr ) index ,
111
+ out IntPtr namep ,
112
+ out IntPtr valuep ,
113
+ out IntPtr sizep
114
+ ) ;
115
+
116
+ if ( err != ErrorCode . NoError )
117
+ return false ;
118
+
119
+ name = Util . Marshal . PtrToStringUTF8 ( namep ) ;
120
+
121
+ if ( valuep != IntPtr . Zero )
122
+ {
123
+ value = new ReadOnlySpan < byte > ( valuep . ToPointer ( ) , ( int ) sizep ) ;
124
+ }
125
+
126
+ return true ;
127
+ }
128
+ }
129
+ }
0 commit comments