@@ -63,55 +63,134 @@ pub enum MemoryError {
6363/// Newtype that implements [`ReadVolatile`] and [`WriteVolatile`] if `T` implements `Read` or
6464/// `Write` respectively, by reading/writing using a bounce buffer, and memcpy-ing into the
6565/// [`VolatileSlice`].
66+ ///
67+ /// Bounce buffers are allocated on the heap, as on-stack bounce buffers could cause stack
68+ /// overflows. If `N == 0` then bounce buffers will be allocated on demand.
6669#[ derive( Debug ) ]
67- pub struct MaybeBounce < T > ( pub T , pub bool ) ;
70+ pub struct MaybeBounce < T , const N : usize = 0 > {
71+ pub ( crate ) target : T ,
72+ persistent_buffer : Option < Box < [ u8 ; N ] > > ,
73+ }
74+
75+ impl < T > MaybeBounce < T , 0 > {
76+ /// Creates a new `MaybeBounce` that always allocates a bounce
77+ /// buffer on-demand
78+ pub fn new ( target : T , should_bounce : bool ) -> Self {
79+ MaybeBounce :: new_persistent ( target, should_bounce)
80+ }
81+ }
82+
83+ impl < T , const N : usize > MaybeBounce < T , N > {
84+ /// Creates a new `MaybeBounce` that uses a persistent, fixed size bounce buffer
85+ /// of size `N`. If a read/write request exceeds the size of this bounce buffer, it
86+ /// is split into multiple, `<= N`-size read/writes.
87+ pub fn new_persistent ( target : T , should_bounce : bool ) -> Self {
88+ let mut bounce = MaybeBounce {
89+ target,
90+ persistent_buffer : None ,
91+ } ;
92+
93+ if should_bounce {
94+ bounce. activate ( )
95+ }
96+
97+ bounce
98+ }
99+
100+ /// Activates this [`MaybeBounce`] to start doing reads/writes via a bounce buffer,
101+ /// which is allocated on the heap by this function (e.g. if `activate()` is never called,
102+ /// no bounce buffer is ever allocated).
103+ pub fn activate ( & mut self ) {
104+ self . persistent_buffer = Some ( vec ! [ 0u8 ; N ] . into_boxed_slice ( ) . try_into ( ) . unwrap ( ) )
105+ }
106+ }
68107
69108// FIXME: replace AsFd with ReadVolatile once &File: ReadVolatile in vm-memory.
70- impl < T : Read + AsFd > ReadVolatile for MaybeBounce < T > {
109+ impl < T : Read + AsFd , const N : usize > ReadVolatile for MaybeBounce < T , N > {
71110 fn read_volatile < B : BitmapSlice > (
72111 & mut self ,
73112 buf : & mut VolatileSlice < B > ,
74113 ) -> Result < usize , VolatileMemoryError > {
75- if self . 1 {
76- let mut bbuf = vec ! [ 0 ; buf. len( ) ] ;
77- let n = self
78- . 0
79- . read ( bbuf. as_mut_slice ( ) )
80- . map_err ( VolatileMemoryError :: IOError ) ?;
81- buf. copy_from ( & bbuf[ ..n] ) ;
82- Ok ( n)
114+ if let Some ( ref mut persistent) = self . persistent_buffer {
115+ let mut bbuf = ( N == 0 ) . then ( || vec ! [ 0u8 ; buf. len( ) ] ) ;
116+ let bbuf = bbuf. as_deref_mut ( ) . unwrap_or ( persistent. as_mut_slice ( ) ) ;
117+
118+ let mut buf = buf. offset ( 0 ) ?;
119+ let mut total = 0 ;
120+ while !buf. is_empty ( ) {
121+ let how_much = buf. len ( ) . min ( bbuf. len ( ) ) ;
122+ let n = self
123+ . target
124+ . read ( & mut bbuf[ ..how_much] )
125+ . map_err ( VolatileMemoryError :: IOError ) ?;
126+ buf. copy_from ( & bbuf[ ..n] ) ;
127+
128+ buf = buf. offset ( n) ?;
129+ total += n;
130+
131+ if n < how_much {
132+ break ;
133+ }
134+ }
135+
136+ Ok ( total)
83137 } else {
84- self . 0 . as_fd ( ) . read_volatile ( buf)
138+ self . target . as_fd ( ) . read_volatile ( buf)
85139 }
86140 }
87141}
88142
89- impl < T : Write + AsFd > WriteVolatile for MaybeBounce < T > {
143+ impl < T : Write + AsFd , const N : usize > WriteVolatile for MaybeBounce < T , N > {
90144 fn write_volatile < B : BitmapSlice > (
91145 & mut self ,
92146 buf : & VolatileSlice < B > ,
93147 ) -> Result < usize , VolatileMemoryError > {
94- if self . 1 {
95- let mut bbuf = vec ! [ 0 ; buf. len( ) ] ;
96- buf. copy_to ( bbuf. as_mut_slice ( ) ) ;
97- self . 0
98- . write ( bbuf. as_slice ( ) )
99- . map_err ( VolatileMemoryError :: IOError )
148+ if let Some ( ref mut persistent) = self . persistent_buffer {
149+ let mut bbuf = ( N == 0 ) . then ( || vec ! [ 0u8 ; buf. len( ) ] ) ;
150+ let bbuf = bbuf. as_deref_mut ( ) . unwrap_or ( persistent. as_mut_slice ( ) ) ;
151+
152+ let mut buf = buf. offset ( 0 ) ?;
153+ let mut total = 0 ;
154+ while !buf. is_empty ( ) {
155+ let how_much = buf. copy_to ( bbuf) ;
156+ let n = self
157+ . target
158+ . write ( & bbuf[ ..how_much] )
159+ . map_err ( VolatileMemoryError :: IOError ) ?;
160+ buf = buf. offset ( n) ?;
161+ total += n;
162+
163+ if n < how_much {
164+ break ;
165+ }
166+ }
167+
168+ Ok ( total)
100169 } else {
101- self . 0 . as_fd ( ) . write_volatile ( buf)
170+ self . target . as_fd ( ) . write_volatile ( buf)
102171 }
103172 }
104173}
105174
106- impl < R : Read > Read for MaybeBounce < R > {
175+ impl < R : Read , const N : usize > Read for MaybeBounce < R , N > {
107176 fn read ( & mut self , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
108- self . 0 . read ( buf)
177+ self . target . read ( buf)
178+ }
179+ }
180+
181+ impl < W : Write , const N : usize > Write for MaybeBounce < W , N > {
182+ fn write ( & mut self , buf : & [ u8 ] ) -> std:: io:: Result < usize > {
183+ self . target . write ( buf)
184+ }
185+
186+ fn flush ( & mut self ) -> std:: io:: Result < ( ) > {
187+ self . target . flush ( )
109188 }
110189}
111190
112- impl < S : Seek > Seek for MaybeBounce < S > {
191+ impl < S : Seek , const N : usize > Seek for MaybeBounce < S , N > {
113192 fn seek ( & mut self , pos : SeekFrom ) -> std:: io:: Result < u64 > {
114- self . 0 . seek ( pos)
193+ self . target . seek ( pos)
115194 }
116195}
117196
@@ -947,30 +1026,45 @@ mod tests {
9471026 fn test_bounce ( ) {
9481027 let file_direct = TempFile :: new ( ) . unwrap ( ) ;
9491028 let file_bounced = TempFile :: new ( ) . unwrap ( ) ;
1029+ let file_persistent_bounced = TempFile :: new ( ) . unwrap ( ) ;
9501030
9511031 let mut data = ( 0 ..=255 ) . collect_vec ( ) ;
9521032
953- MaybeBounce ( file_direct. as_file ( ) , false )
1033+ MaybeBounce :: new ( file_direct. as_file ( ) , false )
1034+ . write_all_volatile ( & VolatileSlice :: from ( data. as_mut_slice ( ) ) )
1035+ . unwrap ( ) ;
1036+ MaybeBounce :: new ( file_bounced. as_file ( ) , true )
9541037 . write_all_volatile ( & VolatileSlice :: from ( data. as_mut_slice ( ) ) )
9551038 . unwrap ( ) ;
956- MaybeBounce ( file_bounced . as_file ( ) , true )
1039+ MaybeBounce :: < _ , 7 > :: new_persistent ( file_persistent_bounced . as_file ( ) , true )
9571040 . write_all_volatile ( & VolatileSlice :: from ( data. as_mut_slice ( ) ) )
9581041 . unwrap ( ) ;
9591042
9601043 let mut data_direct = vec ! [ 0u8 ; 256 ] ;
9611044 let mut data_bounced = vec ! [ 0u8 ; 256 ] ;
1045+ let mut data_persistent_bounced = vec ! [ 0u8 ; 256 ] ;
9621046
9631047 file_direct. as_file ( ) . seek ( SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
9641048 file_bounced. as_file ( ) . seek ( SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
1049+ file_persistent_bounced
1050+ . as_file ( )
1051+ . seek ( SeekFrom :: Start ( 0 ) )
1052+ . unwrap ( ) ;
9651053
966- MaybeBounce ( file_direct. as_file ( ) , false )
1054+ MaybeBounce :: new ( file_direct. as_file ( ) , false )
9671055 . read_exact_volatile ( & mut VolatileSlice :: from ( data_direct. as_mut_slice ( ) ) )
9681056 . unwrap ( ) ;
969- MaybeBounce ( file_bounced. as_file ( ) , true )
1057+ MaybeBounce :: new ( file_bounced. as_file ( ) , true )
9701058 . read_exact_volatile ( & mut VolatileSlice :: from ( data_bounced. as_mut_slice ( ) ) )
9711059 . unwrap ( ) ;
1060+ MaybeBounce :: < _ , 7 > :: new_persistent ( file_persistent_bounced. as_file ( ) , true )
1061+ . read_exact_volatile ( & mut VolatileSlice :: from (
1062+ data_persistent_bounced. as_mut_slice ( ) ,
1063+ ) )
1064+ . unwrap ( ) ;
9721065
9731066 assert_eq ! ( data_direct, data_bounced) ;
9741067 assert_eq ! ( data_direct, data) ;
1068+ assert_eq ! ( data_persistent_bounced, data) ;
9751069 }
9761070}
0 commit comments