Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GO: Implement Bitpos command #3407

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* Go: Add Cluster Scan support ([#3295](https://github.com/valkey-io/valkey-glide/pull/3295))
* Go: Fix unsafe precondition violation for the slice::from_raw_parts ([#3350](https://github.com/valkey-io/valkey-glide/issues/3350))
* Go: Add `GeoAdd` and the Geospatial interface ([#3366](https://github.com/valkey-io/valkey-glide/pull/3366))
* Go: Add `BITPOS` ([#3407](https://github.com/valkey-io/valkey-glide/pull/3407))
* Go: Add `FLUSHALL` ([#3117](https://github.com/valkey-io/valkey-glide/pull/3117))
* Go: Add `FLUSHDB` ([#3117](https://github.com/valkey-io/valkey-glide/pull/3117))
* Go: Add password update api ([#3346](https://github.com/valkey-io/valkey-glide/pull/3346))
Expand Down
48 changes: 48 additions & 0 deletions go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5680,6 +5680,54 @@ func (client *baseClient) XClaimJustIdWithOptions(
return handleStringArrayResponse(result)
}

// Returns the position of the first bit matching the given bit value.
//
// Parameters:
//
// key - The key of the string.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix idents in docs for both functions

// bit - The bit value to match. The value must be 0 or 1.
//
// Return value:
//
// The position of the first occurrence matching bit in the binary value of
// the string held at key. If bit is not found, a -1 is returned.
//
// [valkey.io]: https://valkey.io/commands/bitpos/
func (client *baseClient) BitPos(key string, bit int64) (int64, error) {
result, err := client.executeCommand(C.BitPos, []string{key, utils.IntToString(bit)})
if err != nil {
return defaultIntResponse, err
}
return handleIntResponse(result)
}

// Returns the position of the first bit matching the given bit value.
//
// Parameters:
//
// key - The key of the string.
// bit - The bit value to match. The value must be 0 or 1.
// bitposOptions - The [BitPosOptions] type.
//
// Return value:
//
// The position of the first occurrence matching bit in the binary value of
// the string held at key. If bit is not found, a -1 is returned.
//
// [valkey.io]: https://valkey.io/commands/bitpos/
func (client *baseClient) BitPosWithOptions(key string, bit int64, bitposOptions options.BitPosOptions) (int64, error) {
optionArgs, err := bitposOptions.ToArgs()
if err != nil {
return defaultIntResponse, err
}
commandArgs := append([]string{key, utils.IntToString(bit)}, optionArgs...)
result, err := client.executeCommand(C.BitPos, commandArgs)
if err != nil {
return defaultIntResponse, err
}
return handleIntResponse(result)
}

// Copies the value stored at the source to the destination key if the
// destination key does not yet exist.
//
Expand Down
4 changes: 4 additions & 0 deletions go/api/bitmap_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type BitmapCommands interface {

BitCountWithOptions(key string, options options.BitCountOptions) (int64, error)

BitPos(key string, bit int64) (int64, error)

BitPosWithOptions(key string, bit int64, options options.BitPosOptions) (int64, error)

BitField(key string, subCommands []options.BitFieldSubCommands) ([]Result[int64], error)

BitFieldRO(key string, commands []options.BitFieldROCommands) ([]Result[int64], error)
Expand Down
65 changes: 65 additions & 0 deletions go/api/bitmap_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,68 @@ func ExampleGlideClusterClient_BitFieldRO() {

// output: [{24 false}]
}

func ExampleGlideClient_BitPos() {
var client *GlideClient = getExampleGlideClient() // example helper function

client.SetBit("my_key", 7, 1)

result, err := client.BitPos("my_key", 1)
if err != nil {
fmt.Println("Glide example failed with an error: ", err)
}
fmt.Println(result)

// Output: 7
}

func ExampleGlideClusterClient_BitPos() {
var client *GlideClusterClient = getExampleGlideClusterClient() // example helper function

client.SetBit("my_key", 7, 1)

result, err := client.BitPos("my_key", 1)
if err != nil {
fmt.Println("Glide example failed with an error: ", err)
}
fmt.Println(result)

// Output: 7
}

func ExampleGlideClient_BitPosWithOptions() {
var client *GlideClient = getExampleGlideClient() // example helper function

client.Set("my_key", "\x00\x01\x00")

options := options.NewBitPosOptions().
SetStart(0).
SetEnd(1)

result, err := client.BitPosWithOptions("my_key", 1, *options)
if err != nil {
fmt.Println("Glide example failed with an error: ", err)
}
fmt.Println(result)

// Output: 15
}

func ExampleGlideClusterClient_BitPosWithOptions() {
var client *GlideClusterClient = getExampleGlideClusterClient() // example helper function

client.Set("my_key", "\x00\x10\x00")

options := options.NewBitPosOptions().
SetStart(10).
SetEnd(14).
SetBitmapIndexType(options.BIT)

result, err := client.BitPosWithOptions("my_key", 1, *options)
if err != nil {
fmt.Println("Glide example failed with an error: ", err)
}
fmt.Println(result)

// Output: 11
}
55 changes: 55 additions & 0 deletions go/api/options/bitpos_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package options

import (
"github.com/valkey-io/valkey-glide/go/utils"
)

// Optional arguments to `BitPos` in [BitMapCommands]
type BitPosOptions struct {
start *int64
end *int64
bitMapIndexType BitmapIndexType
}

func NewBitPosOptions() *BitPosOptions {
return &BitPosOptions{}
}

// SetStart defines start byte to calculate bitpos in bitpos command.
func (options *BitPosOptions) SetStart(start int64) *BitPosOptions {
options.start = &start
return options
}

// SetEnd defines end byte to calculate bitpos in bitpos command.
func (options *BitPosOptions) SetEnd(end int64) *BitPosOptions {
options.end = &end
return options
}

// SetBitmapIndexType to specify start and end are in BYTE or BIT
func (options *BitPosOptions) SetBitmapIndexType(bitMapIndexType BitmapIndexType) *BitPosOptions {
options.bitMapIndexType = bitMapIndexType
return options
}

// ToArgs converts the options to a list of arguments.
func (opts *BitPosOptions) ToArgs() ([]string, error) {
args := []string{}
var err error

if opts.start != nil {
args = append(args, utils.IntToString(*opts.start))
if opts.end != nil {
args = append(args, utils.IntToString(*opts.end))
if opts.bitMapIndexType != "" {
if opts.bitMapIndexType == BIT || opts.bitMapIndexType == BYTE {
args = append(args, string(opts.bitMapIndexType))
}
}
}
}
return args, err
}
94 changes: 94 additions & 0 deletions go/integTest/shared_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8242,6 +8242,100 @@ func (suite *GlideTestSuite) TestBitField_MultipleOperations() {
})
}

func (suite *GlideTestSuite) TestBitPos_ExistingKey() {
suite.runWithDefaultClients(func(client api.BaseClient) {
key := uuid.New().String()
client.Set(key, "\x10")
result, err := client.BitPos(key, 1)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), int64(3), result)
})
}

func (suite *GlideTestSuite) TestBitPos_NonExistingKey() {
suite.runWithDefaultClients(func(client api.BaseClient) {
key := uuid.New().String()
result, err := client.BitPos(key, 0)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), int64(0), result)
})
}

func (suite *GlideTestSuite) TestBitPosWithOptions_StartEnd() {
suite.runWithDefaultClients(func(client api.BaseClient) {
key := uuid.New().String()
client.Set(key, "\x00\x01\x80")

opts := options.NewBitPosOptions().
SetStart(0).
SetEnd(1)

result, err := client.BitPosWithOptions(key, 1, *opts)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), int64(15), result)
})
}

func (suite *GlideTestSuite) TestBitPosWithOptions_BitmapIndexType() {
suite.SkipIfServerVersionLowerThanBy("7.0.0")
suite.runWithDefaultClients(func(client api.BaseClient) {
key := uuid.New().String()
client.Set(key, "\x00\x02\x00")

opts := options.NewBitPosOptions().
SetStart(1).
SetEnd(2).
SetBitmapIndexType(options.BYTE)

result, err := client.BitPosWithOptions(key, 1, *opts)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), int64(14), result)
})
}

func (suite *GlideTestSuite) TestBitPosWithOptions_BitIndexType() {
suite.SkipIfServerVersionLowerThanBy("7.0.0")
suite.runWithDefaultClients(func(client api.BaseClient) {
key := uuid.New().String()
client.Set(key, "\x00\x10\x00")

opts := options.NewBitPosOptions().
SetStart(10).
SetEnd(14).
SetBitmapIndexType(options.BIT)

result, err := client.BitPosWithOptions(key, 1, *opts)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), int64(11), result)
})
}

func (suite *GlideTestSuite) TestBitPos_FindBitZero() {
suite.runWithDefaultClients(func(client api.BaseClient) {
key := uuid.New().String()
client.Set(key, "\xFF\xF7")

result, err := client.BitPos(key, 0)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), int64(12), result)
})
}

func (suite *GlideTestSuite) TestBitPosWithOptions_NegativeEnd() {
suite.runWithDefaultClients(func(client api.BaseClient) {
key := uuid.New().String()
client.Set(key, "\x00\x01\x80")

opts := options.NewBitPosOptions().
SetStart(0).
SetEnd(-2)

result, err := client.BitPosWithOptions(key, 1, *opts)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), int64(15), result)
})
}

func (suite *GlideTestSuite) TestBitField_Failures() {
suite.runWithDefaultClients(func(client api.BaseClient) {
key := uuid.New().String()
Expand Down
Loading