@@ -11,7 +11,7 @@ use glide_core::{
1111} ;
1212use std:: {
1313 ffi:: { CStr , CString , c_char, c_void} ,
14- slice,
14+ slice:: from_raw_parts ,
1515 sync:: Arc ,
1616} ;
1717use tokio:: runtime:: { Builder , Runtime } ;
@@ -444,9 +444,9 @@ pub unsafe extern "C" fn init(level: Option<Level>, file_name: *const c_char) ->
444444///
445445/// # Safety
446446/// * `client_ptr` must be a valid Client pointer from create_client
447- /// * `cursor` must be a valid C string
447+ /// * `cursor` must be "0" for initial scan or a valid cursor ID from previous scan
448448/// * `args` and `arg_lengths` must be valid arrays of length `arg_count`
449- /// * `args` array format: alternating parameter names and values (e.g., [b"MATCH", pattern, b"COUNT", count_str] )
449+ /// * `args` format: [b"MATCH", pattern, b"COUNT", count, b"TYPE", type] (all optional )
450450#[ unsafe( no_mangle) ]
451451pub unsafe extern "C-unwind" fn request_cluster_scan (
452452 client_ptr : * const c_void ,
@@ -456,6 +456,7 @@ pub unsafe extern "C-unwind" fn request_cluster_scan(
456456 args : * const usize ,
457457 arg_lengths : * const u64 ,
458458) {
459+ // Build client and add panic guard.
459460 let client = unsafe {
460461 Arc :: increment_strong_count ( client_ptr) ;
461462 Arc :: from_raw ( client_ptr as * mut Client )
@@ -468,21 +469,34 @@ pub unsafe extern "C-unwind" fn request_cluster_scan(
468469 callback_index,
469470 } ;
470471
472+ // Build arguments and get the cluster scan state.
471473 let cursor_id = unsafe { CStr :: from_ptr ( cursor) }
472474 . to_str ( )
473475 . unwrap_or ( "0" )
474476 . to_owned ( ) ;
475477
476- let cluster_scan_args = unsafe { parse_cluster_scan_args ( args, arg_lengths, arg_count) } ;
478+ let cluster_scan_args = match unsafe {
479+ build_cluster_scan_args (
480+ arg_count,
481+ args,
482+ arg_lengths,
483+ core. failure_callback ,
484+ callback_index,
485+ )
486+ } {
487+ Some ( args) => args,
488+ None => return ,
489+ } ;
477490
478491 let scan_state_cursor =
479492 match glide_core:: cluster_scan_container:: get_cluster_scan_cursor ( cursor_id) {
480493 Ok ( existing_cursor) => existing_cursor,
481494 Err ( _error) => redis:: ScanStateRC :: new ( ) ,
482495 } ;
483496
497+ // Run cluster scan.
484498 client. runtime . spawn ( async move {
485- let mut panic_guard = PanicGuard {
499+ let mut async_panic_guard = PanicGuard {
486500 panicked : true ,
487501 failure_callback : core. failure_callback ,
488502 callback_index,
@@ -502,12 +516,13 @@ pub unsafe extern "C-unwind" fn request_cluster_scan(
502516 report_error (
503517 core. failure_callback ,
504518 callback_index,
505- error_message ( & err) ,
506- error_type ( & err) ,
519+ glide_core :: errors :: error_message ( & err) ,
520+ glide_core :: errors :: error_type ( & err) ,
507521 ) ;
508522 } ,
509523 } ;
510- panic_guard. panicked = false ;
524+
525+ async_panic_guard. panicked = false ;
511526 } ) ;
512527
513528 panic_guard. panicked = false ;
@@ -531,69 +546,168 @@ pub unsafe extern "C" fn remove_cluster_scan_cursor(cursor_id: *const c_char) {
531546 }
532547}
533548
534- /// Parse cluster scan arguments from C-style arrays.
549+ /// Build cluster scan arguments from C-style arrays.
535550///
536551/// # Safety
537552/// * `args` and `arg_lengths` must be valid arrays of length `arg_count`
538553/// * Each pointer in `args` must point to valid memory of the corresponding length
539- unsafe fn parse_cluster_scan_args (
554+ unsafe fn build_cluster_scan_args (
555+ arg_count : u64 ,
540556 args : * const usize ,
541557 arg_lengths : * const u64 ,
542- arg_count : u64 ,
543- ) -> redis:: ClusterScanArgs {
558+ failure_callback : FailureCallback ,
559+ callback_index : usize ,
560+ ) -> Option < redis:: ClusterScanArgs > {
544561 if arg_count == 0 {
545- return redis:: ClusterScanArgs :: builder ( ) . build ( ) ;
562+ return Some ( redis:: ClusterScanArgs :: builder ( ) . build ( ) ) ;
546563 }
547564
548- let mut pattern: Option < & [ u8 ] > = None ;
549- let mut object_type: Option < & [ u8 ] > = None ;
550- let mut count: Option < & [ u8 ] > = None ;
551-
552- let mut i = 0 ;
553- while i < arg_count as usize {
554- let arg_ptr = unsafe { * args. add ( i) as * const u8 } ;
555- let arg_len = unsafe { * arg_lengths. add ( i) as usize } ;
556- let arg = unsafe { slice:: from_raw_parts ( arg_ptr, arg_len) } ;
557-
558- match arg {
559- b"MATCH" if i + 1 < arg_count as usize => {
560- i += 1 ;
561- let pattern_ptr = unsafe { * args. add ( i) as * const u8 } ;
562- let pattern_len = unsafe { * arg_lengths. add ( i) as usize } ;
563- pattern = Some ( unsafe { slice:: from_raw_parts ( pattern_ptr, pattern_len) } ) ;
565+ let arg_vec = unsafe { convert_double_pointer_to_vec ( args, arg_count, arg_lengths) } ;
566+
567+ let mut pattern: & [ u8 ] = & [ ] ;
568+ let mut object_type: & [ u8 ] = & [ ] ;
569+ let mut count: & [ u8 ] = & [ ] ;
570+
571+ let mut iter = arg_vec. iter ( ) . peekable ( ) ;
572+ while let Some ( arg) = iter. next ( ) {
573+ match * arg {
574+ b"MATCH" => match iter. next ( ) {
575+ Some ( pat) => pattern = pat,
576+ None => {
577+ unsafe {
578+ report_error (
579+ failure_callback,
580+ callback_index,
581+ "No argument following MATCH." . into ( ) ,
582+ RequestErrorType :: Unspecified ,
583+ ) ;
584+ }
585+ return None ;
586+ }
587+ } ,
588+ b"TYPE" => match iter. next ( ) {
589+ Some ( obj_type) => object_type = obj_type,
590+ None => {
591+ unsafe {
592+ report_error (
593+ failure_callback,
594+ callback_index,
595+ "No argument following TYPE." . into ( ) ,
596+ RequestErrorType :: Unspecified ,
597+ ) ;
598+ }
599+ return None ;
600+ }
601+ } ,
602+ b"COUNT" => match iter. next ( ) {
603+ Some ( c) => count = c,
604+ None => {
605+ unsafe {
606+ report_error (
607+ failure_callback,
608+ callback_index,
609+ "No argument following COUNT." . into ( ) ,
610+ RequestErrorType :: Unspecified ,
611+ ) ;
612+ }
613+ return None ;
614+ }
615+ } ,
616+ _ => {
617+ unsafe {
618+ report_error (
619+ failure_callback,
620+ callback_index,
621+ "Unknown cluster scan argument" . into ( ) ,
622+ RequestErrorType :: Unspecified ,
623+ ) ;
624+ }
625+ return None ;
564626 }
565- b"TYPE" if i + 1 < arg_count as usize => {
566- i += 1 ;
567- let type_ptr = unsafe { * args. add ( i) as * const u8 } ;
568- let type_len = unsafe { * arg_lengths. add ( i) as usize } ;
569- object_type = Some ( unsafe { slice:: from_raw_parts ( type_ptr, type_len) } ) ;
627+ }
628+ }
629+
630+ // Convert back to proper types
631+ let converted_count = match std:: str:: from_utf8 ( count) {
632+ Ok ( v) => {
633+ if !count. is_empty ( ) {
634+ match v. parse :: < u32 > ( ) {
635+ Ok ( v) => v,
636+ Err ( _) => {
637+ unsafe {
638+ report_error (
639+ failure_callback,
640+ callback_index,
641+ "Invalid COUNT value" . into ( ) ,
642+ RequestErrorType :: Unspecified ,
643+ ) ;
644+ }
645+ return None ;
646+ }
647+ }
648+ } else {
649+ 10 // default count value
570650 }
571- b"COUNT" if i + 1 < arg_count as usize => {
572- i += 1 ;
573- let count_ptr = unsafe { * args. add ( i) as * const u8 } ;
574- let count_len = unsafe { * arg_lengths. add ( i) as usize } ;
575- count = Some ( unsafe { slice:: from_raw_parts ( count_ptr, count_len) } ) ;
651+ }
652+ Err ( _) => {
653+ unsafe {
654+ report_error (
655+ failure_callback,
656+ callback_index,
657+ "Invalid UTF-8 in COUNT argument" . into ( ) ,
658+ RequestErrorType :: Unspecified ,
659+ ) ;
576660 }
577- _ => { }
661+ return None ;
578662 }
579- i += 1 ;
580- }
663+ } ;
581664
582- let mut builder = redis:: ClusterScanArgs :: builder ( ) ;
583- if let Some ( pattern) = pattern {
584- builder = builder. with_match_pattern ( pattern) ;
585- }
586- if let Some ( count_bytes) = count {
587- if let Ok ( count_str) = std:: str:: from_utf8 ( count_bytes) {
588- if let Ok ( count_val) = count_str. parse :: < u32 > ( ) {
589- builder = builder. with_count ( count_val) ;
665+ let converted_type = match std:: str:: from_utf8 ( object_type) {
666+ Ok ( v) => redis:: ObjectType :: from ( v. to_string ( ) ) ,
667+ Err ( _) => {
668+ unsafe {
669+ report_error (
670+ failure_callback,
671+ callback_index,
672+ "Invalid UTF-8 in TYPE argument" . into ( ) ,
673+ RequestErrorType :: Unspecified ,
674+ ) ;
590675 }
676+ return None ;
591677 }
678+ } ;
679+
680+ let mut cluster_scan_args_builder = redis:: ClusterScanArgs :: builder ( ) ;
681+ if !count. is_empty ( ) {
682+ cluster_scan_args_builder = cluster_scan_args_builder. with_count ( converted_count) ;
592683 }
593- if let Some ( type_bytes) = object_type {
594- if let Ok ( type_str) = std:: str:: from_utf8 ( type_bytes) {
595- builder = builder. with_object_type ( redis:: ObjectType :: from ( type_str. to_string ( ) ) ) ;
596- }
684+ if !pattern. is_empty ( ) {
685+ cluster_scan_args_builder = cluster_scan_args_builder. with_match_pattern ( pattern) ;
686+ }
687+ if !object_type. is_empty ( ) {
688+ cluster_scan_args_builder = cluster_scan_args_builder. with_object_type ( converted_type) ;
689+ }
690+ Some ( cluster_scan_args_builder. build ( ) )
691+ }
692+
693+ /// Converts a double pointer to a vec.
694+ ///
695+ /// # Safety
696+ ///
697+ /// `convert_double_pointer_to_vec` returns a `Vec` of u8 slice which holds pointers of C
698+ /// strings. The returned `Vec<&'a [u8]>` is meant to be copied into Rust code. Storing them
699+ /// for later use will cause the program to crash as the pointers will be freed by the caller.
700+ unsafe fn convert_double_pointer_to_vec < ' a > (
701+ data : * const usize ,
702+ len : u64 ,
703+ data_len : * const u64 ,
704+ ) -> Vec < & ' a [ u8 ] > {
705+ let string_ptrs = unsafe { from_raw_parts ( data, len as usize ) } ;
706+ let string_lengths = unsafe { from_raw_parts ( data_len, len as usize ) } ;
707+ let mut result = Vec :: < & [ u8 ] > :: with_capacity ( string_ptrs. len ( ) ) ;
708+ for ( i, & str_ptr) in string_ptrs. iter ( ) . enumerate ( ) {
709+ let slice = unsafe { from_raw_parts ( str_ptr as * const u8 , string_lengths[ i] as usize ) } ;
710+ result. push ( slice) ;
597711 }
598- builder . build ( )
712+ result
599713}
0 commit comments