4
4
using System ;
5
5
using System . Buffers ;
6
6
using System . Collections . Generic ;
7
+ using System . Diagnostics ;
7
8
using System . Linq ;
8
9
using Garnet . common ;
9
10
using Tsavorite . core ;
@@ -418,5 +419,216 @@ private void ListSet(ref ObjectInput input, ref SpanByteAndMemory output)
418
419
output . Length = ( int ) ( output_currptr - output_startptr ) ;
419
420
}
420
421
}
422
+
423
+ private void ListPosition ( ref ObjectInput input , ref SpanByteAndMemory output )
424
+ {
425
+ var element = input . parseState . GetArgSliceByRef ( input . parseStateStartIdx ) . ReadOnlySpan ;
426
+ input . parseStateStartIdx ++ ;
427
+
428
+ var isMemory = false ;
429
+ MemoryHandle ptrHandle = default ;
430
+ var output_startptr = output . SpanByte . ToPointer ( ) ;
431
+ var output_currptr = output_startptr ;
432
+ var output_end = output_currptr + output . Length ;
433
+ var count = 0 ;
434
+ var isDefaultCount = true ;
435
+ ObjectOutputHeader outputHeader = default ;
436
+
437
+ try
438
+ {
439
+ if ( ! ReadListPositionInput ( ref input , out var rank , out count , out isDefaultCount , out var maxlen , out var error ) )
440
+ {
441
+ while ( ! RespWriteUtils . WriteError ( error , ref output_currptr , output_end ) )
442
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
443
+ return ;
444
+ }
445
+
446
+ if ( count < 0 )
447
+ {
448
+ while ( ! RespWriteUtils . WriteError ( CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER , ref output_currptr , output_end ) )
449
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
450
+ return ;
451
+ }
452
+
453
+ if ( maxlen < 0 )
454
+ {
455
+ while ( ! RespWriteUtils . WriteError ( CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER , ref output_currptr , output_end ) )
456
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
457
+ return ;
458
+ }
459
+
460
+ if ( rank == 0 )
461
+ {
462
+ while ( ! RespWriteUtils . WriteError ( CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER , ref output_currptr , output_end ) )
463
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
464
+ return ;
465
+ }
466
+
467
+ count = count == 0 ? list . Count : count ;
468
+ var totalArrayHeaderLen = 0 ;
469
+ var lastFoundItemIndex = - 1 ;
470
+
471
+ if ( ! isDefaultCount )
472
+ {
473
+ while ( ! RespWriteUtils . WriteArrayLength ( count , ref output_currptr , output_end , out var _ , out totalArrayHeaderLen ) )
474
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
475
+ }
476
+
477
+ var noOfFoundItem = 0 ;
478
+ if ( rank > 0 )
479
+ {
480
+ var currentNode = list . First ;
481
+ var currentIndex = 0 ;
482
+ var maxlenIndex = maxlen == 0 ? list . Count : maxlen ;
483
+ do
484
+ {
485
+ var nextNode = currentNode . Next ;
486
+ if ( currentNode . Value . AsSpan ( ) . SequenceEqual ( element ) )
487
+ {
488
+ if ( rank == 1 )
489
+ {
490
+ lastFoundItemIndex = currentIndex ;
491
+ while ( ! RespWriteUtils . WriteInteger ( currentIndex , ref output_currptr , output_end ) )
492
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
493
+
494
+ noOfFoundItem ++ ;
495
+ if ( noOfFoundItem == count )
496
+ {
497
+ break ;
498
+ }
499
+ }
500
+ else
501
+ {
502
+ rank -- ;
503
+ }
504
+ }
505
+ currentNode = nextNode ;
506
+ currentIndex ++ ;
507
+ }
508
+ while ( currentNode != null && currentIndex < maxlenIndex ) ;
509
+ }
510
+ else // (rank < 0)
511
+ {
512
+ var currentNode = list . Last ;
513
+ var currentIndex = list . Count - 1 ;
514
+ var maxlenIndex = maxlen == 0 ? 0 : list . Count - maxlen ;
515
+ do
516
+ {
517
+ var nextNode = currentNode . Previous ;
518
+ if ( currentNode . Value . AsSpan ( ) . SequenceEqual ( element ) )
519
+ {
520
+ if ( rank == - 1 )
521
+ {
522
+ lastFoundItemIndex = currentIndex ;
523
+ while ( ! RespWriteUtils . WriteInteger ( currentIndex , ref output_currptr , output_end ) )
524
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
525
+
526
+ noOfFoundItem ++ ;
527
+ if ( noOfFoundItem == count )
528
+ {
529
+ break ;
530
+ }
531
+ }
532
+ else
533
+ {
534
+ rank ++ ;
535
+ }
536
+ }
537
+ currentNode = nextNode ;
538
+ currentIndex -- ;
539
+ }
540
+ while ( currentNode != null && currentIndex >= maxlenIndex ) ;
541
+ }
542
+
543
+ if ( isDefaultCount && noOfFoundItem == 0 )
544
+ {
545
+ output_currptr = output_startptr ;
546
+ while ( ! RespWriteUtils . WriteNull ( ref output_currptr , output_end ) )
547
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
548
+ }
549
+ else if ( ! isDefaultCount && noOfFoundItem == 0 )
550
+ {
551
+ output_currptr = output_startptr ;
552
+ while ( ! RespWriteUtils . WriteNullArray ( ref output_currptr , output_end ) )
553
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
554
+ }
555
+ else if ( ! isDefaultCount && noOfFoundItem != count )
556
+ {
557
+ var newTotalArrayHeaderLen = 0 ;
558
+ var startOutputStartptr = output_startptr ;
559
+ RespWriteUtils . WriteArrayLength ( noOfFoundItem , ref startOutputStartptr , output_end , out var _ , out newTotalArrayHeaderLen ) ; // ReallocateOutput is not needed here as there should be always be available space in the output buffer as we have already written the max array length
560
+ Debug . Assert ( totalArrayHeaderLen >= newTotalArrayHeaderLen , "newTotalArrayHeaderLen can't be bigger than totalArrayHeaderLen as we have already written max array lenght in the buffer" ) ;
561
+
562
+ if ( totalArrayHeaderLen != newTotalArrayHeaderLen )
563
+ {
564
+ var remainingLength = ( output_currptr - output_startptr ) - totalArrayHeaderLen ;
565
+ Buffer . MemoryCopy ( output_startptr + totalArrayHeaderLen , output_startptr + newTotalArrayHeaderLen , remainingLength , remainingLength ) ;
566
+ output_currptr = output_currptr - ( totalArrayHeaderLen - newTotalArrayHeaderLen ) ;
567
+ }
568
+ }
569
+
570
+ outputHeader . result1 = noOfFoundItem ;
571
+ }
572
+ finally
573
+ {
574
+ while ( ! RespWriteUtils . WriteDirect ( ref outputHeader , ref output_currptr , output_end ) )
575
+ ObjectUtils . ReallocateOutput ( ref output , ref isMemory , ref output_startptr , ref ptrHandle , ref output_currptr , ref output_end ) ;
576
+
577
+ if ( isMemory )
578
+ ptrHandle . Dispose ( ) ;
579
+ output . Length = ( int ) ( output_currptr - output_startptr ) ;
580
+ }
581
+ }
582
+
583
+ private static unsafe bool ReadListPositionInput ( ref ObjectInput input , out int rank , out int count , out bool isDefaultCount , out int maxlen , out ReadOnlySpan < byte > error )
584
+ {
585
+ var currTokenIdx = input . parseStateStartIdx ;
586
+
587
+ rank = 1 ; // By default, LPOS takes first match element
588
+ count = 1 ; // By default, LPOS return 1 element
589
+ isDefaultCount = true ;
590
+ maxlen = 0 ; // By default, iterate to all the item
591
+
592
+ error = default ;
593
+
594
+ while ( currTokenIdx < input . parseState . Count )
595
+ {
596
+ var sbParam = input . parseState . GetArgSliceByRef ( currTokenIdx ++ ) . ReadOnlySpan ;
597
+
598
+ if ( sbParam . SequenceEqual ( CmdStrings . RANK ) || sbParam . SequenceEqual ( CmdStrings . rank ) )
599
+ {
600
+ if ( ! input . parseState . TryGetInt ( currTokenIdx ++ , out rank ) )
601
+ {
602
+ error = CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER ;
603
+ return false ;
604
+ }
605
+ }
606
+ else if ( sbParam . SequenceEqual ( CmdStrings . COUNT ) || sbParam . SequenceEqual ( CmdStrings . count ) )
607
+ {
608
+ if ( ! input . parseState . TryGetInt ( currTokenIdx ++ , out count ) )
609
+ {
610
+ error = CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER ;
611
+ return false ;
612
+ }
613
+
614
+ isDefaultCount = false ;
615
+ }
616
+ else if ( sbParam . SequenceEqual ( CmdStrings . MAXLEN ) || sbParam . SequenceEqual ( CmdStrings . maxlen ) )
617
+ {
618
+ if ( ! input . parseState . TryGetInt ( currTokenIdx ++ , out maxlen ) )
619
+ {
620
+ error = CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER ;
621
+ return false ;
622
+ }
623
+ }
624
+ else
625
+ {
626
+ error = CmdStrings . RESP_SYNTAX_ERROR ;
627
+ return false ;
628
+ }
629
+ }
630
+
631
+ return true ;
632
+ }
421
633
}
422
634
}
0 commit comments