@@ -138,7 +138,7 @@ private bool NetworkStringSetBit<TGarnetApi>(ref TGarnetApi storageApi)
138138 var sbKey = parseState . GetArgSliceByRef ( 0 ) . SpanByte ;
139139
140140 // Validate offset
141- if ( ! parseState . TryGetLong ( 1 , out var offset ) || ( offset < 0 ) )
141+ if ( ! parseState . TryGetLong ( 1 , out var offset ) || ( offset < 0 ) || ! BitmapManager . IsValidBitOffset ( offset ) )
142142 {
143143 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_BITOFFSET_IS_NOT_INTEGER ) ;
144144 }
@@ -178,7 +178,7 @@ private bool NetworkStringGetBit<TGarnetApi>(ref TGarnetApi storageApi)
178178 var sbKey = parseState . GetArgSliceByRef ( 0 ) . SpanByte ;
179179
180180 // Validate offset
181- if ( ! parseState . TryGetLong ( 1 , out var offset ) || ( offset < 0 ) )
181+ if ( ! parseState . TryGetLong ( 1 , out var offset ) || ( offset < 0 ) || ! BitmapManager . IsValidBitOffset ( offset ) )
182182 {
183183 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_BITOFFSET_IS_NOT_INTEGER ) ;
184184 }
@@ -205,29 +205,43 @@ private bool NetworkStringBitCount<TGarnetApi>(ref TGarnetApi storageApi)
205205 where TGarnetApi : IGarnetApi
206206 {
207207 var count = parseState . Count ;
208- if ( count < 1 || count > 4 )
208+ if ( count is not 1 and not 3 and not 4 )
209209 {
210210 return AbortWithWrongNumberOfArguments ( nameof ( RespCommand . BITCOUNT ) ) ;
211211 }
212212
213213 // <[Get Key]>
214214 var sbKey = parseState . GetArgSliceByRef ( 0 ) . SpanByte ;
215215
216- // Validate start & end offsets, if exist
217- if ( parseState . Count > 1 )
216+ // Extract parameters in command order:
217+ // start, end, [BIT|BYTE]
218+ var useBitIndex = true ;
219+ if ( count > 1 )
218220 {
219- if ( ! parseState . TryGetInt ( 1 , out _ ) || ( parseState . Count > 2 && ! parseState . TryGetInt ( 2 , out _ ) ) )
221+ if ( ! parseState . TryGetLong ( 1 , out _ ) || ! parseState . TryGetLong ( 2 , out _ ) )
220222 {
221223 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER ) ;
222224 }
223- }
224225
225- var input = new RawStringInput ( RespCommand . BITCOUNT , ref parseState , startIdx : 1 ) ;
226+ if ( count > 3 )
227+ {
228+ var spanOffsetType = parseState . GetArgSliceByRef ( 3 ) . ReadOnlySpan ;
229+ if ( spanOffsetType . EqualsUpperCaseSpanIgnoringCase ( "BIT"u8 ) )
230+ useBitIndex = true ;
231+ else if ( spanOffsetType . EqualsUpperCaseSpanIgnoringCase ( "BYTE"u8 ) )
232+ useBitIndex = false ;
233+ else
234+ {
235+ return AbortWithErrorMessage ( CmdStrings . RESP_SYNTAX_ERROR ) ;
236+ }
226237
227- var o = new SpanByteAndMemory ( dcurr , ( int ) ( dend - dcurr ) ) ;
228238
229- var status = storageApi . StringBitCount ( ref sbKey , ref input , ref o ) ;
239+ }
240+ }
230241
242+ var input = new RawStringInput ( RespCommand . BITCOUNT , ref parseState , startIdx : 1 , arg1 : useBitIndex ? 1 : 0 ) ;
243+ var o = new SpanByteAndMemory ( dcurr , ( int ) ( dend - dcurr ) ) ;
244+ var status = storageApi . StringBitCount ( ref sbKey , ref input , ref o ) ;
231245 if ( status == GarnetStatus . OK )
232246 {
233247 if ( ! o . IsSpanByte )
@@ -251,7 +265,7 @@ private bool NetworkStringBitPosition<TGarnetApi>(ref TGarnetApi storageApi)
251265 where TGarnetApi : IGarnetApi
252266 {
253267 var count = parseState . Count ;
254- if ( count < 2 || count > 5 )
268+ if ( count is < 2 or > 5 )
255269 {
256270 return AbortWithWrongNumberOfArguments ( nameof ( RespCommand . BITPOS ) ) ;
257271 }
@@ -266,31 +280,57 @@ private bool NetworkStringBitPosition<TGarnetApi>(ref TGarnetApi storageApi)
266280 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_BIT_IS_NOT_INTEGER ) ;
267281 }
268282
269- // Validate start & end offsets, if exist
270- if ( parseState . Count > 2 )
283+ long startOffset = 0 ;
284+ long endOffset = - 1 ;
285+ byte offsetType = 0x0 ;
286+ var hasStartOffset = false ;
287+ var hasEndOffset = false ;
288+
289+ // Extract parameters in command order, consistent with PrivateMethods BITPOS decoding:
290+ // bit, start, end, [BIT|BYTE]
291+ if ( count > 2 )
271292 {
272- if ( ! parseState . TryGetInt ( 2 , out _ ) ||
273- ( parseState . Count > 3 && ! parseState . TryGetInt ( 3 , out _ ) ) )
293+ // start
294+ if ( ! parseState . TryGetLong ( 2 , out startOffset ) )
274295 {
275296 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER ) ;
276297 }
277- }
298+ hasStartOffset = true ;
278299
279- // Validate offset range type (BIT / BYTE), if exists
280- if ( parseState . Count > 4 )
281- {
282- var sbOffsetType = parseState . GetArgSliceByRef ( 4 ) . ReadOnlySpan ;
283- if ( ! sbOffsetType . EqualsUpperCaseSpanIgnoringCase ( "BIT"u8 ) &&
284- ! sbOffsetType . EqualsUpperCaseSpanIgnoringCase ( "BYTE"u8 ) )
300+ if ( count > 3 )
285301 {
286- return AbortWithErrorMessage ( CmdStrings . RESP_SYNTAX_ERROR ) ;
302+ // end
303+ if ( ! parseState . TryGetLong ( 3 , out endOffset ) )
304+ {
305+ return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER ) ;
306+ }
307+ hasEndOffset = true ;
308+
309+ if ( count > 4 )
310+ {
311+ // Optional index type (default BYTE)
312+ var sbOffsetType = parseState . GetArgSliceByRef ( 4 ) . ReadOnlySpan ;
313+ if ( sbOffsetType . EqualsUpperCaseSpanIgnoringCase ( "BIT"u8 ) )
314+ {
315+ offsetType = 0x1 ;
316+ }
317+ else if ( ! sbOffsetType . EqualsUpperCaseSpanIgnoringCase ( "BYTE"u8 ) )
318+ {
319+ return AbortWithErrorMessage ( CmdStrings . RESP_SYNTAX_ERROR ) ;
320+ }
321+ }
287322 }
288323 }
289324
290- var input = new RawStringInput ( RespCommand . BITPOS , ref parseState , startIdx : 1 ) ;
325+ if ( BitmapManager . TryValidateBitPosOffsets ( startOffset , endOffset , offsetType , hasStartOffset , hasEndOffset ) )
326+ {
327+ while ( ! RespWriteUtils . TryWriteInt64 ( - 1 , ref dcurr , dend ) )
328+ SendAndReset ( ) ;
329+ return true ;
330+ }
291331
332+ var input = new RawStringInput ( RespCommand . BITPOS , ref parseState , startIdx : 1 ) ;
292333 var o = new SpanByteAndMemory ( dcurr , ( int ) ( dend - dcurr ) ) ;
293-
294334 var status = storageApi . StringBitPosition ( ref sbKey , ref input , ref o ) ;
295335
296336 if ( status == GarnetStatus . OK )
@@ -385,17 +425,23 @@ private bool StringBitField<TGarnetApi>(ref TGarnetApi storageApi)
385425
386426 // [GET <encoding> <offset>] [SET <encoding> <offset> <value>] [INCRBY <encoding> <offset> <increment>]
387427 // Process encoding argument
388- if ( ( currTokenIdx >= parseState . Count ) || ! parseState . TryGetBitfieldEncoding ( currTokenIdx , out _ , out _ ) )
428+ if ( ( currTokenIdx >= parseState . Count ) || ! parseState . TryGetBitfieldEncoding ( currTokenIdx , out var bitCount , out _ ) )
389429 {
390430 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_INVALID_BITFIELD_TYPE ) ;
391431 }
392432 var encodingSlice = parseState . GetArgSliceByRef ( currTokenIdx ++ ) ;
393433
394434 // Process offset argument
395- if ( ( currTokenIdx >= parseState . Count ) || ! parseState . TryGetBitfieldOffset ( currTokenIdx , out _ , out _ ) )
435+ if ( ( currTokenIdx >= parseState . Count ) || ! parseState . TryGetBitfieldOffset ( currTokenIdx , out var offset , out var multiplyOffset ) )
436+ {
437+ return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_BITOFFSET_IS_NOT_INTEGER ) ;
438+ }
439+
440+ if ( ! BitmapManager . TryValidateBitfieldOffset ( offset , ( byte ) bitCount , multiplyOffset , out _ , out _ ) )
396441 {
397442 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_BITOFFSET_IS_NOT_INTEGER ) ;
398443 }
444+
399445 var offsetSlice = parseState . GetArgSliceByRef ( currTokenIdx ++ ) ;
400446
401447 // GET Subcommand takes 2 args, encoding and offset
@@ -467,17 +513,23 @@ private bool StringBitFieldReadOnly<TGarnetApi>(ref TGarnetApi storageApi)
467513 // GET Subcommand takes 2 args, encoding and offset
468514
469515 // Process encoding argument
470- if ( ( currTokenIdx >= parseState . Count ) || ! parseState . TryGetBitfieldEncoding ( currTokenIdx , out _ , out _ ) )
516+ if ( ( currTokenIdx >= parseState . Count ) || ! parseState . TryGetBitfieldEncoding ( currTokenIdx , out var bitCount , out _ ) )
471517 {
472518 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_INVALID_BITFIELD_TYPE ) ;
473519 }
474520 var encodingSlice = parseState . GetArgSliceByRef ( currTokenIdx ++ ) ;
475521
476522 // Process offset argument
477- if ( ( currTokenIdx >= parseState . Count ) || ! parseState . TryGetBitfieldOffset ( currTokenIdx , out _ , out _ ) )
523+ if ( ( currTokenIdx >= parseState . Count ) || ! parseState . TryGetBitfieldOffset ( currTokenIdx , out var offset , out var multiplyOffset ) )
478524 {
479525 return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_BITOFFSET_IS_NOT_INTEGER ) ;
480526 }
527+
528+ if ( ! BitmapManager . TryValidateBitfieldOffset ( offset , ( byte ) bitCount , multiplyOffset , out _ , out _ ) )
529+ {
530+ return AbortWithErrorMessage ( CmdStrings . RESP_ERR_GENERIC_BITOFFSET_IS_NOT_INTEGER ) ;
531+ }
532+
481533 var offsetSlice = parseState . GetArgSliceByRef ( currTokenIdx ++ ) ;
482534
483535 secondaryCommandArgs . Add ( ( RespCommand . GET , [ commandSlice , encodingSlice , offsetSlice ] ) ) ;
0 commit comments