@@ -20,9 +20,9 @@ use std::{
20
20
} ;
21
21
22
22
#[ allow( unused) ] // different code path for WASI
23
- use tempfile:: { tempdir, Builder , NamedTempFile , TempDir } ;
23
+ use tempfile:: { tempdir, Builder , NamedTempFile , SpooledTempFile , TempDir } ;
24
24
25
- use crate :: { asset_io:: rename_or_move, Error , Result } ;
25
+ use crate :: { asset_io:: rename_or_move, settings :: get_settings_value , Error , Result } ;
26
26
// Replace data at arbitrary location and len in a file.
27
27
// start_location is where the replacement data will start
28
28
// replace_len is how many bytes from source to replaced starting a start_location
@@ -118,6 +118,44 @@ pub(crate) fn stream_len<R: Read + Seek + ?Sized>(reader: &mut R) -> Result<u64>
118
118
Ok ( len)
119
119
}
120
120
121
+ #[ cfg( target_arch = "wasm32" ) ]
122
+ fn stream_with_fs_fallback_wasm (
123
+ _threshold_override : Option < usize > ,
124
+ ) -> Result < std:: io:: Cursor < Vec < u8 > > > {
125
+ Ok ( std:: io:: Cursor :: new ( Vec :: new ( ) ) )
126
+ }
127
+
128
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
129
+ fn stream_with_fs_fallback_file_io ( threshold_override : Option < usize > ) -> Result < SpooledTempFile > {
130
+ let threshold = threshold_override. unwrap_or ( get_settings_value :: < usize > (
131
+ "core.backing_store_memory_threshold_in_mb" ,
132
+ ) ?) ;
133
+
134
+ Ok ( SpooledTempFile :: new ( threshold) )
135
+ }
136
+
137
+ /// Will create a [Read], [Write], and [Seek] capable stream that will stay in memory
138
+ /// as long as the threshold is not exceeded. The threshold is specified in MB in the
139
+ /// settings under ""core.backing_store_memory_threshold_in_mb"
140
+ ///
141
+ /// # Parameters
142
+ /// - `threshold_override`: Optional override for the threshold value in MB. If provided, this
143
+ /// value will be used instead of the one from settings.
144
+ ///
145
+ /// # Errors
146
+ /// - Returns an error if the threshold value from settings is not valid.
147
+ ///
148
+ /// # Note
149
+ /// This will return a an in-memory stream when the compilation target doesn't support file I/O.
150
+ pub ( crate ) fn stream_with_fs_fallback (
151
+ threshold_override : Option < usize > ,
152
+ ) -> Result < impl Read + Write + Seek > {
153
+ #[ cfg( target_arch = "wasm32" ) ]
154
+ return stream_with_fs_fallback_wasm ( threshold_override) ;
155
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
156
+ return stream_with_fs_fallback_file_io ( threshold_override) ;
157
+ }
158
+
121
159
// Returns a new Vec first making sure it can hold the desired capacity. Fill
122
160
// with default value if provided
123
161
pub ( crate ) fn safe_vec < T : Clone > ( item_cnt : u64 , init_with : Option < T > ) -> Result < Vec < T > > {
@@ -386,4 +424,41 @@ mod tests {
386
424
)
387
425
. is_err( ) ) ;
388
426
}
427
+
428
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
429
+ #[ test]
430
+ fn test_safe_stream_threshold_behavior ( ) {
431
+ let mut stream = stream_with_fs_fallback_file_io ( Some ( 10 ) ) . unwrap ( ) ;
432
+
433
+ // Less data written than required to write to the FS.
434
+ let small_data = b"small" ; // 5 bytes
435
+ stream. write_all ( small_data) . unwrap ( ) ;
436
+ assert ! ( !stream. is_rolled( ) , "data still in memory" ) ;
437
+
438
+ // Adds more data to exceed the threshold.
439
+ let large_data = b"this is larger than 10 bytes total" ;
440
+ stream. write_all ( large_data) . unwrap ( ) ;
441
+ assert ! ( stream. is_rolled( ) , "data moved to disk" ) ;
442
+ }
443
+
444
+ #[ cfg( not( target_arch = "wasm32" ) ) ]
445
+ #[ test]
446
+ fn test_safe_stream_no_threshold_behavior ( ) {
447
+ let mut stream = stream_with_fs_fallback_file_io ( None ) . unwrap ( ) ;
448
+
449
+ // Less data written than required to write to the FS.
450
+ let small_data = b"small" ; // 5 bytes
451
+ stream. write_all ( small_data) . unwrap ( ) ;
452
+ assert ! ( !stream. is_rolled( ) , "data still in memory" ) ;
453
+
454
+ let large_data = vec ! [ 0 ; 1024 * 1024 ] ; // 1MB.
455
+ let threshold =
456
+ get_settings_value :: < usize > ( "core.backing_store_memory_threshold_in_mb" ) . unwrap ( ) ;
457
+
458
+ for _ in 0 ..threshold {
459
+ stream. write_all ( & large_data) . unwrap ( ) ;
460
+ }
461
+
462
+ assert ! ( stream. is_rolled( ) , "data moved to disk" ) ;
463
+ }
389
464
}
0 commit comments