1+ // Copyright (c) Microsoft Corporation.
2+ // Licensed under the MIT license.
3+
4+ using System ;
5+ using System . Collections . Generic ;
6+ using System . Diagnostics ;
7+ using System . Runtime . InteropServices ;
8+ using System . Text ;
9+
10+ namespace Garnet . server
11+ {
12+ /// <summary>
13+ /// Represents a simplified version of RESP command's information
14+ /// </summary>
15+ public struct SimpleRespCommandInfo
16+ {
17+ /// <summary>
18+ /// Command Arity
19+ /// </summary>
20+ public sbyte Arity ;
21+
22+ /// <summary>
23+ /// True if command is allowed in a transaction context
24+ /// </summary>
25+ public bool AllowedInTxn ;
26+
27+ /// <summary>
28+ /// True if command has sub-commands
29+ /// </summary>
30+ public bool IsParent ;
31+
32+ /// <summary>
33+ /// True if command is a sub-command
34+ /// </summary>
35+ public bool IsSubCommand ;
36+
37+ /// <summary>
38+ /// Simplified command key specifications
39+ /// </summary>
40+ public SimpleRespKeySpec [ ] KeySpecs ;
41+
42+ /// <summary>
43+ /// Store type that the command operates on (None/Main/Object/All). Default: None for commands without key arguments.
44+ /// </summary>
45+ public StoreType StoreType ;
46+
47+ /// <summary>
48+ /// Default SimpleRespCommandInfo
49+ /// </summary>
50+ public static SimpleRespCommandInfo Default = new ( ) ;
51+ }
52+
53+ /// <summary>
54+ /// Represents a simplified version of a single key specification of a RESP command
55+ /// </summary>
56+ public struct SimpleRespKeySpecBeginSearch
57+ {
58+ /// <summary>
59+ /// Keyword that precedes the keys in the command arguments
60+ /// </summary>
61+ public byte [ ] Keyword ;
62+
63+ /// <summary>
64+ /// Index of first key or the index at which to start searching for keyword (if begin search is of keyword type)
65+ /// </summary>
66+ public int Index ;
67+
68+ /// <summary>
69+ /// If true - begin search is of type index, otherwise begin search is of type keyword
70+ /// </summary>
71+ public bool IsIndexType ;
72+
73+ /// <summary>
74+ /// Set begin search of type index
75+ /// </summary>
76+ /// <param name="index">Index of first key</param>
77+ public SimpleRespKeySpecBeginSearch ( int index )
78+ {
79+ IsIndexType = true ;
80+ Index = index ;
81+ }
82+
83+ /// <summary>
84+ /// Set begin search of type keyword
85+ /// </summary>
86+ /// <param name="keyword">Keyword that precedes the keys in the command arguments</param>
87+ /// <param name="startIdx">Index at which to start searching for keyword</param>
88+ public SimpleRespKeySpecBeginSearch ( string keyword , int startIdx )
89+ {
90+ Index = startIdx ;
91+ Keyword = Encoding . UTF8 . GetBytes ( keyword ) ;
92+ }
93+ }
94+
95+ /// <summary>
96+ /// Represents a simplified version of a single key specification of a RESP command
97+ /// </summary>
98+ [ StructLayout ( LayoutKind . Explicit , Size = Size ) ]
99+ public struct SimpleRespKeySpecFindKeys
100+ {
101+ /// <summary>
102+ /// Size of struct
103+ /// </summary>
104+ public const int Size = 10 ;
105+
106+ /// <summary>
107+ /// The index (relative to begin search) of the argument containing the number of keys
108+ /// </summary>
109+ [ FieldOffset ( 0 ) ]
110+ public int KeyNumIndex ;
111+
112+ /// <summary>
113+ /// the index (relative to begin search) of the first key
114+ /// </summary>
115+ [ FieldOffset ( 4 ) ]
116+ public int FirstKey ;
117+
118+ /// <summary>
119+ /// The index (relative to begin search) of the last key argument or limit - stops the key search by a factor.
120+ /// 0 and 1 mean no limit. 2 means half of the remaining arguments, 3 means a third, and so on.
121+ /// Limit is used if IsRangeLimitType is set.
122+ /// </summary>
123+ [ FieldOffset ( 0 ) ]
124+ public int LastKeyOrLimit ;
125+
126+ /// <summary>
127+ /// The number of arguments that should be skipped, after finding a key, to find the next one.
128+ /// </summary>
129+ [ FieldOffset ( 4 ) ]
130+ public int KeyStep ;
131+
132+ /// <summary>
133+ /// If true - find keys is of type range, otherwise find keys is of type keynum
134+ /// </summary>
135+ [ FieldOffset ( 8 ) ]
136+ public bool IsRangeType ;
137+
138+ /// <summary>
139+ /// If true - find keys is of type range and limit is used, otherwise find keys is of type range and last key is used.
140+ /// </summary>
141+ [ FieldOffset ( 9 ) ]
142+ public bool IsRangeLimitType ;
143+
144+ /// <summary>
145+ /// Set find keys of type range
146+ /// </summary>
147+ /// <param name="keyStep">The number of arguments that should be skipped, after finding a key, to find the next one</param>
148+ /// <param name="lastKeyOrLimit">The index of the last key argument or the limit</param>
149+ /// <param name="isLimit">If preceding argument represents a limit</param>
150+ public SimpleRespKeySpecFindKeys ( int keyStep , int lastKeyOrLimit , bool isLimit )
151+ {
152+ IsRangeType = true ;
153+ KeyStep = keyStep ;
154+ LastKeyOrLimit = lastKeyOrLimit ;
155+ IsRangeLimitType = isLimit ;
156+ }
157+
158+ /// <summary>
159+ /// Set find keys of type keynum
160+ /// </summary>
161+ /// <param name="keyNumIndex">The index of the argument containing the number of keys</param>
162+ /// <param name="firstKey">The index of the first key</param>
163+ /// <param name="keyStep">The number of arguments that should be skipped, after finding a key, to find the next one</param>
164+ public SimpleRespKeySpecFindKeys ( int keyNumIndex , int firstKey , int keyStep )
165+ {
166+ KeyNumIndex = keyNumIndex ;
167+ FirstKey = firstKey ;
168+ KeyStep = keyStep ;
169+ }
170+ }
171+
172+ /// <summary>
173+ /// Represents a simplified version of a single key specification of a RESP command
174+ /// </summary>
175+ public struct SimpleRespKeySpec
176+ {
177+ /// <summary>
178+ /// Begin search specification
179+ /// </summary>
180+ public SimpleRespKeySpecBeginSearch BeginSearch ;
181+
182+ /// <summary>
183+ /// Find keys specification
184+ /// </summary>
185+ public SimpleRespKeySpecFindKeys FindKeys ;
186+
187+ /// <summary>
188+ /// Key specification flags
189+ /// </summary>
190+ public KeySpecificationFlags Flags ;
191+ }
192+
193+ /// <summary>
194+ /// Extension methods for obtaining simplified RESP command info structs
195+ /// </summary>
196+ public static class RespCommandInfoExtensions
197+ {
198+ /// <summary>
199+ /// Populates a SimpleRespCommandInfo struct from a RespCommandsInfo instance
200+ /// </summary>
201+ /// <param name="cmdInfo">The source RespCommandsInfo</param>
202+ /// <param name="simpleCmdInfo">The destination SimpleRespCommandInfo</param>
203+ public static void PopulateSimpleCommandInfo ( this RespCommandsInfo cmdInfo , ref SimpleRespCommandInfo simpleCmdInfo )
204+ {
205+ var arity = cmdInfo . Arity ;
206+
207+ // Verify that arity is in the signed byte range (-128 to 127)
208+ Debug . Assert ( arity is <= sbyte . MaxValue and >= sbyte . MinValue ) ;
209+
210+ simpleCmdInfo . Arity = ( sbyte ) arity ;
211+ simpleCmdInfo . AllowedInTxn = ( cmdInfo . Flags & RespCommandFlags . NoMulti ) == 0 ;
212+ simpleCmdInfo . IsParent = ( cmdInfo . SubCommands ? . Length ?? 0 ) > 0 ;
213+ simpleCmdInfo . IsSubCommand = cmdInfo . Parent != null ;
214+ simpleCmdInfo . StoreType = cmdInfo . StoreType ;
215+
216+ if ( cmdInfo . KeySpecifications != null )
217+ {
218+ var tmpSimpleKeySpecs = new List < SimpleRespKeySpec > ( ) ;
219+
220+ foreach ( var keySpec in cmdInfo . KeySpecifications )
221+ {
222+ if ( keySpec . TryGetSimpleKeySpec ( out var simpleKeySpec ) )
223+ tmpSimpleKeySpecs . Add ( simpleKeySpec ) ;
224+ }
225+
226+ simpleCmdInfo . KeySpecs = tmpSimpleKeySpecs . ToArray ( ) ;
227+ }
228+ }
229+
230+ /// <summary>
231+ /// Tries to convert a RespCommandKeySpecification to a SimpleRespKeySpec
232+ /// </summary>
233+ /// <param name="keySpec">The source RespCommandKeySpecification</param>
234+ /// <param name="simpleKeySpec">The resulting SimpleRespKeySpec</param>
235+ /// <returns>True if successful</returns>
236+ public static bool TryGetSimpleKeySpec ( this RespCommandKeySpecification keySpec , out SimpleRespKeySpec simpleKeySpec )
237+ {
238+ simpleKeySpec = new SimpleRespKeySpec ( ) ;
239+
240+ if ( keySpec . BeginSearch is BeginSearchUnknown || keySpec . FindKeys is FindKeysUnknown )
241+ return false ;
242+
243+ simpleKeySpec . BeginSearch = keySpec . BeginSearch switch
244+ {
245+ BeginSearchIndex bsi => new SimpleRespKeySpecBeginSearch ( bsi . Index ) ,
246+ BeginSearchKeyword bsk => new SimpleRespKeySpecBeginSearch ( bsk . Keyword , bsk . StartFrom ) ,
247+ _ => throw new NotSupportedException ( )
248+ } ;
249+
250+ simpleKeySpec . FindKeys = keySpec . FindKeys switch
251+ {
252+ FindKeysRange fkr => fkr . LastKey == - 1
253+ ? new SimpleRespKeySpecFindKeys ( fkr . KeyStep , fkr . Limit , true )
254+ : new SimpleRespKeySpecFindKeys ( fkr . KeyStep , fkr . LastKey , false ) ,
255+ FindKeysKeyNum fkk => new SimpleRespKeySpecFindKeys ( fkk . KeyNumIdx , fkk . FirstKey , fkk . KeyStep ) ,
256+ _ => throw new NotSupportedException ( )
257+ } ;
258+
259+ simpleKeySpec . Flags = keySpec . Flags ;
260+
261+ return true ;
262+ }
263+ }
264+ }
0 commit comments