22
33//! Utilities for creating new [`DevicePaths`].
44//!
5- //! This module contains [`DevicePathBuilder`], as well as submodules
6- //! containing types for building each type of device path node.
5+ //! This module provides builders to construct device paths, as well as
6+ //! submodules containing types for building each type of device path node.
7+ //!
8+ //! - [`DevicePathBuilder`]
9+ //! - [`OwnedDevicePathBuilder`]
710//!
811//! [`DevicePaths`]: DevicePath
912
@@ -15,7 +18,10 @@ use core::fmt::{self, Display, Formatter};
1518use core:: mem:: MaybeUninit ;
1619
1720#[ cfg( feature = "alloc" ) ]
18- use alloc:: vec:: Vec ;
21+ use {
22+ alloc:: { boxed:: Box , vec:: Vec } ,
23+ core:: mem,
24+ } ;
1925
2026/// A builder for [`DevicePaths`].
2127///
@@ -74,14 +80,14 @@ use alloc::vec::Vec;
7480/// ```
7581#[ derive( Debug ) ]
7682pub struct DevicePathBuilder < ' a > {
77- storage : BuilderStorage < ' a > ,
83+ storage : BuilderStorageRef < ' a > ,
7884}
7985
8086impl < ' a > DevicePathBuilder < ' a > {
8187 /// Create a builder backed by a statically-sized buffer.
8288 pub const fn with_buf ( buf : & ' a mut [ MaybeUninit < u8 > ] ) -> Self {
8389 Self {
84- storage : BuilderStorage :: Buf { buf, offset : 0 } ,
90+ storage : BuilderStorageRef :: Buf { buf, offset : 0 } ,
8591 }
8692 }
8793
@@ -92,30 +98,30 @@ impl<'a> DevicePathBuilder<'a> {
9298 pub fn with_vec ( v : & ' a mut Vec < u8 > ) -> Self {
9399 v. clear ( ) ;
94100 Self {
95- storage : BuilderStorage :: Vec ( v) ,
101+ storage : BuilderStorageRef :: Vec ( v) ,
96102 }
97103 }
98104
99105 /// Add a node to the device path.
100106 ///
101107 /// An error will be returned if an [`END_ENTIRE`] node is passed to
102- /// this function, as that node will be added when ` finalize` is
108+ /// this function, as that node will be added when [`Self:: finalize`] is
103109 /// called.
104110 ///
105111 /// [`END_ENTIRE`]: uefi::proto::device_path::DeviceSubType::END_ENTIRE
106112 pub fn push ( mut self , node : & dyn BuildNode ) -> Result < Self , BuildError > {
107113 let node_size = usize:: from ( node. size_in_bytes ( ) ?) ;
108114
109115 match & mut self . storage {
110- BuilderStorage :: Buf { buf, offset } => {
116+ BuilderStorageRef :: Buf { buf, offset } => {
111117 node. write_data (
112118 buf. get_mut ( * offset..* offset + node_size)
113119 . ok_or ( BuildError :: BufferTooSmall ) ?,
114120 ) ;
115121 * offset += node_size;
116122 }
117123 #[ cfg( feature = "alloc" ) ]
118- BuilderStorage :: Vec ( vec) => {
124+ BuilderStorageRef :: Vec ( vec) => {
119125 let old_size = vec. len ( ) ;
120126 vec. reserve ( node_size) ;
121127 let buf = & mut vec. spare_capacity_mut ( ) [ ..node_size] ;
@@ -138,20 +144,21 @@ impl<'a> DevicePathBuilder<'a> {
138144 let this = self . push ( & end:: Entire ) ?;
139145
140146 let data: & [ u8 ] = match & this. storage {
141- BuilderStorage :: Buf { buf, offset } => unsafe {
147+ BuilderStorageRef :: Buf { buf, offset } => unsafe {
142148 maybe_uninit_slice_assume_init_ref ( & buf[ ..* offset] )
143149 } ,
144150 #[ cfg( feature = "alloc" ) ]
145- BuilderStorage :: Vec ( vec) => vec,
151+ BuilderStorageRef :: Vec ( vec) => vec,
146152 } ;
147153
148154 let ptr: * const ( ) = data. as_ptr ( ) . cast ( ) ;
149155 Ok ( unsafe { & * ptr_meta:: from_raw_parts ( ptr, data. len ( ) ) } )
150156 }
151157}
152158
159+ /// Reference to the backup storage for [`DevicePathBuilder`]
153160#[ derive( Debug ) ]
154- enum BuilderStorage < ' a > {
161+ enum BuilderStorageRef < ' a > {
155162 Buf {
156163 buf : & ' a mut [ MaybeUninit < u8 > ] ,
157164 offset : usize ,
@@ -193,6 +200,58 @@ impl Display for BuildError {
193200
194201impl core:: error:: Error for BuildError { }
195202
203+ /// Variant of [`DevicePathBuilder`] to construct a boxed [`DevicePath`].
204+ ///
205+ /// Using this builder is equivalent to calling [`DevicePath::to_boxed`] on a
206+ /// device path constructed by the normal builder.
207+ #[ derive( Debug ) ]
208+ #[ cfg( feature = "alloc" ) ]
209+ pub struct OwnedDevicePathBuilder {
210+ vec : Vec < u8 > ,
211+ }
212+
213+ #[ cfg( feature = "alloc" ) ]
214+ impl OwnedDevicePathBuilder {
215+ /// Creates a new builder.
216+ pub fn new ( ) -> Self {
217+ let vec = Vec :: new ( ) ;
218+ Self { vec }
219+ }
220+
221+ /// Add a node to the device path.
222+ ///
223+ /// An error will be returned if an [`END_ENTIRE`] node is passed to
224+ /// this function, as that node will be added when [`Self::finalize`] is
225+ /// called.
226+ ///
227+ /// [`END_ENTIRE`]: uefi::proto::device_path::DeviceSubType::END_ENTIRE
228+ pub fn push ( mut self , node : & dyn BuildNode ) -> Result < Self , BuildError > {
229+ let node_size = usize:: from ( node. size_in_bytes ( ) ?) ;
230+
231+ let old_size = self . vec . len ( ) ;
232+ self . vec . reserve ( node_size) ;
233+ let buf = & mut self . vec . spare_capacity_mut ( ) [ ..node_size] ;
234+ node. write_data ( buf) ;
235+ unsafe {
236+ self . vec . set_len ( old_size + node_size) ;
237+ }
238+ Ok ( self )
239+ }
240+
241+ /// Add an [`END_ENTIRE`] node and return the resulting [`DevicePath`].
242+ ///
243+ /// This method consumes the builder.
244+ ///
245+ /// [`END_ENTIRE`]: uefi::proto::device_path::DeviceSubType::END_ENTIRE
246+ pub fn finalize ( self ) -> Result < Box < DevicePath > , BuildError > {
247+ let this = self . push ( & end:: Entire ) ?;
248+ let boxed = this. vec . into_boxed_slice ( ) ;
249+ // SAFETY: This is safe as a DevicePath has the same layout.
250+ let dvp = unsafe { mem:: transmute ( boxed) } ;
251+ Ok ( dvp)
252+ }
253+ }
254+
196255/// Trait for types that can be used to build a node via
197256/// [`DevicePathBuilder::push`].
198257///
@@ -246,11 +305,6 @@ mod tests {
246305 use crate :: proto:: device_path:: messaging:: {
247306 Ipv4AddressOrigin , IscsiLoginOptions , IscsiProtocol , RestServiceAccessMode , RestServiceType ,
248307 } ;
249- use core:: slice;
250-
251- const fn path_to_bytes ( path : & DevicePath ) -> & [ u8 ] {
252- unsafe { slice:: from_raw_parts ( path. as_ffi_ptr ( ) . cast :: < u8 > ( ) , size_of_val ( path) ) }
253- }
254308
255309 /// Test building an ACPI ADR node.
256310 #[ test]
@@ -267,10 +321,8 @@ mod tests {
267321 let node: & crate :: proto:: device_path:: acpi:: Adr =
268322 path. node_iter ( ) . next ( ) . unwrap ( ) . try_into ( ) . unwrap ( ) ;
269323 assert_eq ! ( node. adr( ) . iter( ) . collect:: <Vec <_>>( ) , [ 1 , 2 ] ) ;
270-
271- let bytes = path_to_bytes ( path) ;
272324 #[ rustfmt:: skip]
273- assert_eq ! ( bytes , [
325+ assert_eq ! ( path . as_bytes ( ) , [
274326 // ACPI ADR node
275327 0x02 , 0x03 , 0x0c , 0x00 ,
276328 // Values
@@ -308,9 +360,8 @@ mod tests {
308360 assert_eq ! ( node. uid_str( ) , b"bc\0 " ) ;
309361 assert_eq ! ( node. cid_str( ) , b"def\0 " ) ;
310362
311- let bytes = path_to_bytes ( path) ;
312363 #[ rustfmt:: skip]
313- assert_eq ! ( bytes , [
364+ assert_eq ! ( path . as_bytes ( ) , [
314365 // ACPI Expanded node
315366 0x02 , 0x02 , 0x19 , 0x00 ,
316367 // HID
@@ -365,9 +416,8 @@ mod tests {
365416 assert_eq ! ( node. vendor_guid_and_data( ) . unwrap( ) . 0 , vendor_guid) ;
366417 assert_eq ! ( node. vendor_guid_and_data( ) . unwrap( ) . 1 , & [ 1 , 2 , 3 , 4 , 5 ] ) ;
367418
368- let bytes = path_to_bytes ( path) ;
369419 #[ rustfmt:: skip]
370- assert_eq ! ( bytes , [
420+ assert_eq ! ( path . as_bytes ( ) , [
371421 // Messaging REST Service node.
372422 0x03 , 0x21 , 0x06 , 0x00 ,
373423 // Type and access mode
@@ -428,27 +478,37 @@ mod tests {
428478 /// from the UEFI Specification.
429479 #[ test]
430480 fn test_fibre_channel_ex_device_path_example ( ) -> Result < ( ) , BuildError > {
431- // Arbitrarily choose this test to use a statically-sized
432- // buffer, just to make sure that code path is tested.
433- let mut buf = [ MaybeUninit :: uninit ( ) ; 256 ] ;
434- let path = DevicePathBuilder :: with_buf ( & mut buf)
435- . push ( & acpi:: Acpi {
481+ let nodes: & [ & dyn BuildNode ] = & [
482+ & acpi:: Acpi {
436483 hid : 0x41d0_0a03 ,
437484 uid : 0x0000_0000 ,
438- } ) ?
439- . push ( & hardware:: Pci {
485+ } ,
486+ & hardware:: Pci {
440487 function : 0x00 ,
441488 device : 0x1f ,
442- } ) ?
443- . push ( & messaging:: FibreChannelEx {
489+ } ,
490+ & messaging:: FibreChannelEx {
444491 world_wide_name : [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ,
445492 logical_unit_number : [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] ,
446- } ) ?
493+ } ,
494+ ] ;
495+
496+ // Arbitrarily choose this test to use a statically-sized
497+ // buffer, just to make sure that code path is tested.
498+ let mut buf = [ MaybeUninit :: uninit ( ) ; 256 ] ;
499+ let path1 = DevicePathBuilder :: with_buf ( & mut buf)
500+ . push ( nodes[ 0 ] ) ?
501+ . push ( nodes[ 1 ] ) ?
502+ . push ( nodes[ 2 ] ) ?
503+ . finalize ( ) ?;
504+ let path2 = OwnedDevicePathBuilder :: new ( )
505+ . push ( nodes[ 0 ] ) ?
506+ . push ( nodes[ 1 ] ) ?
507+ . push ( nodes[ 2 ] ) ?
447508 . finalize ( ) ?;
448509
449- let bytes = path_to_bytes ( path) ;
450510 #[ rustfmt:: skip]
451- assert_eq ! ( bytes , [
511+ const EXPECTED : [ u8 ; 46 ] = [
452512 // ACPI node
453513 0x02 , 0x01 , 0x0c , 0x00 ,
454514 // HID
@@ -477,7 +537,10 @@ mod tests {
477537
478538 // End-entire node
479539 0x7f , 0xff , 0x04 , 0x00 ,
480- ] ) ;
540+ ] ;
541+
542+ assert_eq ! ( path1. as_bytes( ) , EXPECTED ) ;
543+ assert_eq ! ( path2. as_bytes( ) , EXPECTED ) ;
481544
482545 Ok ( ( ) )
483546 }
@@ -532,9 +595,8 @@ mod tests {
532595 } ) ?
533596 . finalize ( ) ?;
534597
535- let bytes = path_to_bytes ( path) ;
536598 #[ rustfmt:: skip]
537- assert_eq ! ( bytes , [
599+ assert_eq ! ( path . as_bytes ( ) , [
538600 // ACPI node
539601 0x02 , 0x01 , 0x0c , 0x00 ,
540602 // HID
0 commit comments