1919// DEALINGS IN THE SOFTWARE.
2020
2121use crate :: {
22- codec:: unsigned_varint:: UnsignedVarint ,
2322 error:: ParseError ,
2423 transport:: webrtc:: schema:: { self , webrtc:: message:: Flag } ,
2524} ;
2625
2726use prost:: Message ;
28- use tokio_util:: codec:: { Decoder , Encoder } ;
2927
3028/// WebRTC mesage.
3129#[ derive( Debug ) ]
@@ -39,34 +37,50 @@ pub struct WebRtcMessage {
3937
4038impl WebRtcMessage {
4139 /// Encode WebRTC message with optional flag.
40+ ///
41+ /// Uses a single allocation by pre-calculating the total size and encoding
42+ /// the varint length prefix and protobuf message directly into the output buffer.
4243 pub fn encode ( payload : Vec < u8 > , flag : Option < Flag > ) -> Vec < u8 > {
4344 let protobuf_payload = schema:: webrtc:: Message {
4445 message : ( !payload. is_empty ( ) ) . then_some ( payload) ,
4546 flag : flag. map ( |f| f as i32 ) ,
4647 } ;
47- let mut payload = Vec :: with_capacity ( protobuf_payload. encoded_len ( ) ) ;
48+
49+ // Calculate sizes upfront for single allocation
50+ // usize_buffer() is a fixed [u8; 10] array (max varint size)
51+ const VARINT_MAX_LEN : usize = 10 ;
52+ let protobuf_len = protobuf_payload. encoded_len ( ) ;
53+
54+ // Single allocation for the entire output
55+ let mut out_buf = Vec :: with_capacity ( VARINT_MAX_LEN + protobuf_len) ;
56+
57+ // Encode varint length prefix directly
58+ let mut varint_buf = unsigned_varint:: encode:: usize_buffer ( ) ;
59+ let varint_slice = unsigned_varint:: encode:: usize ( protobuf_len, & mut varint_buf) ;
60+ out_buf. extend_from_slice ( varint_slice) ;
61+
62+ // Encode protobuf directly into output buffer
4863 protobuf_payload
49- . encode ( & mut payload )
64+ . encode ( & mut out_buf )
5065 . expect ( "Vec<u8> to provide needed capacity" ) ;
5166
52- let mut out_buf = bytes:: BytesMut :: with_capacity ( payload. len ( ) + 4 ) ;
53- let mut codec = UnsignedVarint :: new ( None ) ;
54- let _result = codec. encode ( payload. into ( ) , & mut out_buf) ;
55-
56- out_buf. into ( )
67+ out_buf
5768 }
5869
5970 /// Decode payload into [`WebRtcMessage`].
71+ ///
72+ /// Decodes the varint length prefix directly from the slice without allocations,
73+ /// then decodes the protobuf message from the remaining bytes.
6074 pub fn decode ( payload : & [ u8 ] ) -> Result < Self , ParseError > {
61- // TODO: https://github.com/paritytech/litep2p/issues/352 set correct size
62- let mut codec = UnsignedVarint :: new ( None ) ;
63- let mut data = bytes :: BytesMut :: from ( payload ) ;
64- let result = codec
65- . decode ( & mut data )
66- . map_err ( |_| ParseError :: InvalidData ) ?
67- . ok_or ( ParseError :: InvalidData ) ?;
68-
69- match schema:: webrtc:: Message :: decode ( result ) {
75+ // Decode varint length prefix directly from slice (no allocation)
76+ // Returns (decoded_length, remaining_bytes_after_varint)
77+ let ( len , remaining ) =
78+ unsigned_varint :: decode :: usize ( payload ) . map_err ( |_| ParseError :: InvalidData ) ? ;
79+
80+ // Get exactly `len` bytes of protobuf data (no allocation)
81+ let protobuf_data = remaining . get ( ..len ) . ok_or ( ParseError :: InvalidData ) ?;
82+
83+ match schema:: webrtc:: Message :: decode ( protobuf_data ) {
7084 Ok ( message) => Ok ( Self {
7185 payload : message. message ,
7286 flag : message. flag . and_then ( |f| Flag :: try_from ( f) . ok ( ) ) ,
0 commit comments