@@ -393,20 +393,18 @@ unsafe extern "C" fn kv_io_read(
393393 let end_chunk = ( offset + requested_length - 1 ) / kv:: CHUNK_SIZE ;
394394
395395 let mut chunk_keys_to_fetch = Vec :: new( ) ;
396- let mut buffered_chunks: HashMap <usize , Vec <u8 >> = HashMap :: new( ) ;
396+ let mut buffered_chunks: HashMap <usize , & [ u8 ] > = HashMap :: new( ) ;
397+ // Skip fetching chunks already present in the dirty buffer (batch mode) or read cache.
397398 for chunk_idx in start_chunk..=end_chunk {
398- // Check dirty buffer first (batch mode writes).
399399 if state. batch_mode {
400- if let Some ( buffered) = state. dirty_buffer. get( & ( chunk_idx as u32 ) ) {
401- buffered_chunks. insert( chunk_idx, buffered. clone( ) ) ;
400+ if state. dirty_buffer. contains_key( & ( chunk_idx as u32 ) ) {
402401 continue ;
403402 }
404403 }
405- // Check read cache.
406404 let key = kv:: get_chunk_key( file. file_tag, chunk_idx as u32 ) ;
407405 if let Some ( read_cache) = state. read_cache. as_ref( ) {
408406 if let Some ( cached) = read_cache. get( key. as_slice( ) ) {
409- buffered_chunks. insert( chunk_idx, cached. clone ( ) ) ;
407+ buffered_chunks. insert( chunk_idx, cached. as_slice ( ) ) ;
410408 continue ;
411409 }
412410 }
@@ -420,29 +418,26 @@ unsafe extern "C" fn kv_io_read(
420418 }
421419 } else {
422420 match ctx. kv_get( chunk_keys_to_fetch) {
423- Ok ( resp) => {
424- if let Some ( read_cache) = state. read_cache. as_mut( ) {
425- for ( key, value) in resp. keys. iter( ) . zip( resp. values. iter( ) ) {
426- if !value. is_empty( ) {
427- read_cache. insert( key. clone( ) , value. clone( ) ) ;
428- }
429- }
430- }
431- resp
432- }
421+ Ok ( resp) => resp,
433422 Err ( _) => return SQLITE_IOERR_READ ,
434423 }
435424 } ;
436425 let value_map = build_value_map( & resp) ;
437426
438427 for chunk_idx in start_chunk..=end_chunk {
439- let chunk_data: Option <& [ u8 ] > = buffered_chunks
440- . get( & chunk_idx)
441- . map( |v| v. as_slice( ) )
442- . or_else( || {
443- let key = kv:: get_chunk_key( file. file_tag, chunk_idx as u32 ) ;
444- value_map. get( key. as_slice( ) ) . copied( )
445- } ) ;
428+ let chunk_data = if state. batch_mode {
429+ state
430+ . dirty_buffer
431+ . get( & ( chunk_idx as u32 ) )
432+ . map( |buffered| buffered. as_slice( ) )
433+ } else {
434+ None
435+ }
436+ . or_else( || buffered_chunks. get( & chunk_idx) . copied( ) )
437+ . or_else( || {
438+ let chunk_key = kv:: get_chunk_key( file. file_tag, chunk_idx as u32 ) ;
439+ value_map. get( chunk_key. as_slice( ) ) . copied( )
440+ } ) ;
446441 let chunk_offset = chunk_idx * kv:: CHUNK_SIZE ;
447442 let read_start = offset. saturating_sub( chunk_offset) ;
448443 let read_end = std:: cmp:: min( kv:: CHUNK_SIZE , offset + requested_length - chunk_offset) ;
@@ -465,6 +460,16 @@ unsafe extern "C" fn kv_io_read(
465460 }
466461 }
467462
463+ // `resp` is empty when every chunk was served from the dirty buffer or read cache.
464+ // In that case this loop is a no-op.
465+ if let Some ( read_cache) = state. read_cache. as_mut( ) {
466+ for ( key, value) in resp. keys. iter( ) . zip( resp. values. iter( ) ) {
467+ if !value. is_empty( ) {
468+ read_cache. insert( key. clone( ) , value. clone( ) ) ;
469+ }
470+ }
471+ }
472+
468473 let actual_bytes = std:: cmp:: min( requested_length, file_size - offset) ;
469474 if actual_bytes < requested_length {
470475 buf[ actual_bytes..] . fill( 0 ) ;
0 commit comments