4040//!
4141//! let trace_id = [0u8; 16];
4242//! let span_id = [1u8; 8];
43- //! let attrs: &[(u8, &[u8] )] = &[(0, "GET"), (1, "/api/v1")];
43+ //! let attrs: &[(u8, &str )] = &[(0, "GET"), (1, "/api/v1")];
4444//!
4545//! // Publish a new context and save the previously attached one (if any).
4646//! let ctx = ThreadContext::new(trace_id, span_id, attrs);
@@ -176,7 +176,7 @@ pub mod linux {
176176 trace_id : [ u8 ; 16 ] ,
177177 span_id : [ u8 ; 8 ] ,
178178 local_root_span_id : [ u8 ; 8 ] ,
179- attrs : & [ ( u8 , & [ u8 ] ) ] ,
179+ attrs : & [ ( u8 , & str ) ] ,
180180 ) -> Self {
181181 const { assert ! ( size_of:: <ThreadContextRecord >( ) == 640 ) }
182182
@@ -210,15 +210,28 @@ pub mod linux {
210210 /// recovery would require us to be able to rollback to the previous attributes which would
211211 /// hurt the happy path, or leave the record in a inconsistent state. Another possibility
212212 /// would be to error out and reset the record in that situation.
213- fn set_attrs ( & mut self , local_root_span_id : [ u8 ; 8 ] , attributes : & [ ( u8 , & [ u8 ] ) ] ) -> bool {
213+ fn set_attrs ( & mut self , local_root_span_id : [ u8 ; 8 ] , attributes : & [ ( u8 , & str ) ] ) -> bool {
214214 let mut fully_encoded = true ;
215215
216+ const { assert ! ( MAX_ATTRS_DATA_SIZE >= 18 ) }
217+ // The local root span id is provided as raw bytes (can be seen as a big-endian u64),
218+ // but readers will expect a string hex representation. We convert it to a fixed
219+ // 16-characters string in the usual lowercase hex format.
220+ //
221+ // There's currently no easy way to use Rust format capabilities to write directly in a
222+ // fixed-size array. Since the conversion is simple, we do it manually.
223+ const HEX_DIGITS : & [ u8 ; 16 ] = b"0123456789abcdef" ;
216224 self . attrs_data [ 0 ] = ROOT_SPAN_KEY_INDEX ;
217- self . attrs_data [ 1 ] = 8 ;
218- self . attrs_data [ 2 ..10 ] . copy_from_slice ( local_root_span_id. as_slice ( ) ) ;
219- let mut offset = 10 ;
225+ self . attrs_data [ 1 ] = 16 ;
226+ for ( i, & byte) in local_root_span_id. iter ( ) . enumerate ( ) {
227+ self . attrs_data [ 2 + i * 2 ] = HEX_DIGITS [ ( byte >> 4 ) as usize ] ;
228+ self . attrs_data [ 2 + i * 2 + 1 ] = HEX_DIGITS [ ( byte & 0xF ) as usize ] ;
229+ }
220230
221- for & ( key_index, val_bytes) in attributes {
231+ let mut offset = 18 ;
232+
233+ for & ( key_index, val) in attributes {
234+ let val_bytes = val. as_bytes ( ) ;
222235 let val_len = val_bytes. len ( ) ;
223236 let val_len = if val_len > 255 {
224237 fully_encoded = false ;
@@ -234,7 +247,7 @@ pub mod linux {
234247 }
235248
236249 self . attrs_data [ offset] = key_index;
237- // `val_len <= 255` from the check above
250+ // `val_len <= 255` thanks to the `min()`
238251 self . attrs_data [ offset + 1 ] = val_len as u8 ;
239252 self . attrs_data [ offset + 2 ..offset + 2 + val_len]
240253 . copy_from_slice ( & val_bytes[ ..val_len] ) ;
@@ -279,7 +292,7 @@ pub mod linux {
279292 trace_id : [ u8 ; 16 ] ,
280293 span_id : [ u8 ; 8 ] ,
281294 local_root_span_id : [ u8 ; 8 ] ,
282- attrs : & [ ( u8 , & [ u8 ] ) ] ,
295+ attrs : & [ ( u8 , & str ) ] ,
283296 ) -> Self {
284297 Self :: from ( ThreadContextRecord :: new (
285298 trace_id,
@@ -363,7 +376,7 @@ pub mod linux {
363376 trace_id : [ u8 ; 16 ] ,
364377 span_id : [ u8 ; 8 ] ,
365378 local_root_span_id : [ u8 ; 8 ] ,
366- attrs : & [ ( u8 , & [ u8 ] ) ] ,
379+ attrs : & [ ( u8 , & str ) ] ,
367380 ) {
368381 let slot = get_tls_slot ( ) ;
369382
@@ -471,34 +484,33 @@ pub mod linux {
471484 assert_eq ! ( record. trace_id, trace_id) ;
472485 assert_eq ! ( record. span_id, span_id) ;
473486 assert_eq ! ( record. valid. load( Ordering :: Relaxed ) , 1 ) ;
474- // 1 (key) + 1 (len) + 8 (root_span_id bytes ) = 10
475- assert_eq ! ( record. attrs_data_size, 10 ) ;
487+ // 1 (key) + 1 (len) + 16 (root_span_id hex chars ) = 18
488+ assert_eq ! ( record. attrs_data_size, 18 ) ;
476489
477490 let _ = ThreadContext :: detach ( ) ;
478491 }
479492
480493 #[ test]
481494 #[ cfg_attr( miri, ignore) ]
482495 fn attribute_encoding_basic ( ) {
483- let root_span_id = [ 0u8 ; 8 ] ;
484- let attrs: & [ ( u8 , & [ u8 ] ) ] = & [ ( 1 , b"GET" ) , ( 2 , b"/api/v1" ) ] ;
485- ThreadContext :: new ( [ 0u8 ; 16 ] , [ 0u8 ; 8 ] , root_span_id, attrs) . attach ( ) ;
496+ let attrs: & [ ( u8 , & str ) ] = & [ ( 1 , "GET" ) , ( 2 , "/api/v1" ) ] ;
497+ ThreadContext :: new ( [ 0u8 ; 16 ] , [ 0u8 ; 8 ] , [ 0u8 ; 8 ] , attrs) . attach ( ) ;
486498
487499 let ptr = read_tls_context_ptr ( ) ;
488500 assert ! ( !ptr. is_null( ) ) ;
489501 let record = unsafe { & * ptr } ;
490- // 1+1+8 (root_span_id) + 1+1+3 (GET) + 1+1+7 (/api/v1)
491- let expected_size: u16 = ( 2 + 8 + 2 + 3 + 2 + 7 ) as u16 ;
502+ // 1+1+16 (root_span_id hex ) + 1+1+3 (GET) + 1+1+7 (/api/v1)
503+ let expected_size: u16 = ( 2 + 16 + 2 + 3 + 2 + 7 ) as u16 ;
492504 assert_eq ! ( record. attrs_data_size, expected_size) ;
493505 assert_eq ! ( record. attrs_data[ 0 ] , 0 ) ;
494- assert_eq ! ( record. attrs_data[ 1 ] , 8 ) ;
495- assert_eq ! ( & record. attrs_data[ 2 ..10 ] , & root_span_id ) ;
496- assert_eq ! ( record. attrs_data[ 10 ] , 1 ) ;
497- assert_eq ! ( record. attrs_data[ 11 ] , 3 ) ;
498- assert_eq ! ( & record. attrs_data[ 12 .. 15 ] , b"GET" ) ;
499- assert_eq ! ( record. attrs_data[ 15 ] , 2 ) ;
500- assert_eq ! ( record. attrs_data[ 16 ] , 7 ) ;
501- assert_eq ! ( & record. attrs_data[ 17 .. 24 ] , b"/api/v1" ) ;
506+ assert_eq ! ( record. attrs_data[ 1 ] , 16 ) ;
507+ assert_eq ! ( & record. attrs_data[ 2 ..18 ] , b"0000000000000000" ) ;
508+ assert_eq ! ( record. attrs_data[ 18 ] , 1 ) ;
509+ assert_eq ! ( record. attrs_data[ 19 ] , 3 ) ;
510+ assert_eq ! ( & record. attrs_data[ 20 .. 23 ] , b"GET" ) ;
511+ assert_eq ! ( record. attrs_data[ 23 ] , 2 ) ;
512+ assert_eq ! ( record. attrs_data[ 24 ] , 7 ) ;
513+ assert_eq ! ( & record. attrs_data[ 25 .. 32 ] , b"/api/v1" ) ;
502514
503515 let _ = ThreadContext :: detach ( ) ;
504516 }
@@ -508,31 +520,31 @@ pub mod linux {
508520 fn attribute_truncation_on_overflow ( ) {
509521 // Build attributes whose combined encoded size exceeds MAX_ATTRS_DATA_SIZE.
510522 // Each max entry: 1 (key) + 1 (len) + 255 (val) = 257 bytes.
511- // root_span_id: 1 (key) + 1 (len) + 8 ( val) = 10 bytes.
512- // Two such entries: 514 bytes, plus root_span_id: 524 .
513- // A third entry of 100 chars would need 102 bytes, bringing the total to 626 > 612, so
523+ // root_span_id: 1 (key) + 1 (len) + 16 (hex val) = 18 bytes.
524+ // Two such entries: 514 bytes, plus root_span_id: 532 .
525+ // A third entry of 100 chars would need 102 bytes, bringing the total to 634 > 612, so
514526 // the third entry must be dropped.
515- let val_a = b "a". repeat ( 255 ) ; // 257 bytes encoded
516- let val_b = b "b". repeat ( 255 ) ; // 257 bytes encoded → 514 total
517- let val_c = b "c". repeat ( 100 ) ; // 102 bytes encoded → 626 total: must be dropped
518-
519- let attrs: & [ ( u8 , & [ u8 ] ) ] = & [
520- ( 1 , val_a. as_slice ( ) ) ,
521- ( 2 , val_b. as_slice ( ) ) ,
522- ( 3 , val_c. as_slice ( ) ) ,
527+ let val_a = "a" . repeat ( 255 ) ; // 257 bytes encoded
528+ let val_b = "b" . repeat ( 255 ) ; // 257 bytes encoded → 514 total
529+ let val_c = "c" . repeat ( 100 ) ; // 102 bytes encoded → 626 total: must be dropped
530+
531+ let attrs: & [ ( u8 , & str ) ] = & [
532+ ( 1 , val_a. as_str ( ) ) ,
533+ ( 2 , val_b. as_str ( ) ) ,
534+ ( 3 , val_c. as_str ( ) ) ,
523535 ] ;
524536
525537 ThreadContext :: new ( [ 0u8 ; 16 ] , [ 0u8 ; 8 ] , [ 0u8 ; 8 ] , attrs) . attach ( ) ;
526538
527539 let ptr = read_tls_context_ptr ( ) ;
528540 assert ! ( !ptr. is_null( ) ) ;
529541 let record = unsafe { & * ptr } ;
530- // Only the first two entries fit (514 bytes + 10 bytes for root_span_id).
531- assert_eq ! ( record. attrs_data_size, 524 ) ;
532- assert_eq ! ( record. attrs_data[ 10 ] , 1 ) ;
533- assert_eq ! ( record. attrs_data[ 11 ] , 255 ) ;
534- assert_eq ! ( record. attrs_data[ 267 ] , 2 ) ;
535- assert_eq ! ( record. attrs_data[ 268 ] , 255 ) ;
542+ // Only the first two entries fit (514 bytes + 18 bytes for root_span_id).
543+ assert_eq ! ( record. attrs_data_size, 532 ) ;
544+ assert_eq ! ( record. attrs_data[ 18 ] , 1 ) ;
545+ assert_eq ! ( record. attrs_data[ 19 ] , 255 ) ;
546+ assert_eq ! ( record. attrs_data[ 275 ] , 2 ) ;
547+ assert_eq ! ( record. attrs_data[ 276 ] , 255 ) ;
536548
537549 let _ = ThreadContext :: detach ( ) ;
538550 }
@@ -548,7 +560,7 @@ pub mod linux {
548560 let root_span_id2 = [ 0x79u8 ; 8 ] ;
549561
550562 // Updating before any context is attached should be equivalent to `attach()`
551- ThreadContext :: update ( trace_id1, span_id1, root_span_id1, & [ ( 0 , b "v1") ] ) ;
563+ ThreadContext :: update ( trace_id1, span_id1, root_span_id1, & [ ( 0 , "v1" ) ] ) ;
552564
553565 let ptr_before = read_tls_context_ptr ( ) ;
554566 assert ! ( !ptr_before. is_null( ) ) ;
@@ -557,13 +569,13 @@ pub mod linux {
557569 assert_eq ! ( record. span_id, span_id1) ;
558570 assert_eq ! ( record. valid. load( Ordering :: Relaxed ) , 1 ) ;
559571 assert_eq ! ( record. attrs_data[ 0 ] , 0 ) ;
560- assert_eq ! ( record. attrs_data[ 1 ] , 8 ) ;
561- assert_eq ! ( & record. attrs_data[ 2 ..10 ] , & root_span_id1 ) ;
562- assert_eq ! ( record. attrs_data[ 10 ] , 0 ) ;
563- assert_eq ! ( record. attrs_data[ 11 ] , 2 ) ;
564- assert_eq ! ( & record. attrs_data[ 12 .. 14 ] , b"v1" ) ;
572+ assert_eq ! ( record. attrs_data[ 1 ] , 16 ) ;
573+ assert_eq ! ( & record. attrs_data[ 2 ..18 ] , b"7878787878787878" ) ;
574+ assert_eq ! ( record. attrs_data[ 18 ] , 0 ) ;
575+ assert_eq ! ( record. attrs_data[ 19 ] , 2 ) ;
576+ assert_eq ! ( & record. attrs_data[ 20 .. 22 ] , b"v1" ) ;
565577
566- ThreadContext :: update ( trace_id2, span_id2, root_span_id2, & [ ( 0 , b "v2") ] ) ;
578+ ThreadContext :: update ( trace_id2, span_id2, root_span_id2, & [ ( 0 , "v2" ) ] ) ;
567579
568580 let ptr_after = read_tls_context_ptr ( ) ;
569581 assert_eq ! (
@@ -576,11 +588,11 @@ pub mod linux {
576588 assert_eq ! ( record. span_id, span_id2) ;
577589 assert_eq ! ( record. valid. load( Ordering :: Relaxed ) , 1 ) ;
578590 assert_eq ! ( record. attrs_data[ 0 ] , 0 ) ;
579- assert_eq ! ( record. attrs_data[ 1 ] , 8 ) ;
580- assert_eq ! ( & record. attrs_data[ 2 ..10 ] , & root_span_id2 ) ;
581- assert_eq ! ( record. attrs_data[ 10 ] , 0 ) ;
582- assert_eq ! ( record. attrs_data[ 11 ] , 2 ) ;
583- assert_eq ! ( & record. attrs_data[ 12 .. 14 ] , b"v2" ) ;
591+ assert_eq ! ( record. attrs_data[ 1 ] , 16 ) ;
592+ assert_eq ! ( & record. attrs_data[ 2 ..18 ] , b"7979797979797979" ) ;
593+ assert_eq ! ( record. attrs_data[ 18 ] , 0 ) ;
594+ assert_eq ! ( record. attrs_data[ 19 ] , 2 ) ;
595+ assert_eq ! ( & record. attrs_data[ 20 .. 22 ] , b"v2" ) ;
584596
585597 let _ = ThreadContext :: detach ( ) ;
586598 assert ! ( read_tls_context_ptr( ) . is_null( ) ) ;
@@ -603,17 +615,17 @@ pub mod linux {
603615 #[ test]
604616 #[ cfg_attr( miri, ignore) ]
605617 fn long_value_capped_at_255_bytes ( ) {
606- let long_val = b "a". repeat ( 300 ) ;
607- ThreadContext :: new ( [ 0u8 ; 16 ] , [ 0u8 ; 8 ] , [ 0u8 ; 8 ] , & [ ( 0 , long_val. as_slice ( ) ) ] ) . attach ( ) ;
618+ let long_val = "a" . repeat ( 300 ) ;
619+ ThreadContext :: new ( [ 0u8 ; 16 ] , [ 0u8 ; 8 ] , [ 0u8 ; 8 ] , & [ ( 0 , long_val. as_str ( ) ) ] ) . attach ( ) ;
608620
609621 let ptr = read_tls_context_ptr ( ) ;
610622 assert ! ( !ptr. is_null( ) ) ;
611623 let record = unsafe { & * ptr } ;
612- // root_span_id occupies offset 0..10 , then the attr entry starts at 10 : key at [10 ],
613- // len at [11 ]
614- let val_len = record. attrs_data [ 2 + 8 + 1 ] ;
624+ // root_span_id occupies offset 0..18 , then the attr entry starts at 18 : key at [18 ],
625+ // len at [19 ]
626+ let val_len = record. attrs_data [ 2 + 16 + 1 ] ;
615627 assert_eq ! ( val_len, 255 , "value must be capped at 255 bytes" ) ;
616- assert_eq ! ( record. attrs_data_size, 2 + 8 + 2 + 255 ) ;
628+ assert_eq ! ( record. attrs_data_size, 2 + 16 + 2 + 255 ) ;
617629
618630 let _ = ThreadContext :: detach ( ) ;
619631 }
@@ -649,7 +661,7 @@ pub mod linux {
649661 let record = unsafe { & * ptr } ;
650662 assert_eq ! ( record. trace_id, spawned_trace_id) ;
651663 assert_eq ! ( record. span_id, spawned_span_id) ;
652- assert_eq ! ( & record. attrs_data[ 2 ..10 ] , & spawned_root_span_id ) ;
664+ assert_eq ! ( & record. attrs_data[ 2 ..18 ] , b"efefefefefefefef" ) ;
653665
654666 let _ = ThreadContext :: detach ( ) ;
655667 assert ! ( read_tls_context_ptr( ) . is_null( ) ) ;
@@ -670,7 +682,7 @@ pub mod linux {
670682 let record = unsafe { & * ptr } ;
671683 assert_eq ! ( record. trace_id, main_trace_id) ;
672684 assert_eq ! ( record. span_id, main_span_id) ;
673- assert_eq ! ( & record. attrs_data[ 2 ..10 ] , & main_root_span_id ) ;
685+ assert_eq ! ( & record. attrs_data[ 2 ..18 ] , b"3333333333333333" ) ;
674686
675687 barrier. wait ( ) ;
676688
0 commit comments