Skip to content
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 backend/types/view_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const FORMAT_YAML = "YAML"
const FORMAT_XML = "XML"
const FORMAT_HEX = "Hex"
const FORMAT_BINARY = "Binary"
const FORMAT_BITSET = "BitSet"

const DECODE_NONE = "None"
const DECODE_BASE64 = "Base64"
Expand Down
101 changes: 101 additions & 0 deletions backend/utils/convert/bitset_convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package convutil

import (
"fmt"
"math"
"strconv"
"strings"
)

type BitSetConvert struct{}

func (BitSetConvert) Enable() bool {
return true
}

func (BitSetConvert) Encode(str string) (string, bool) {
var result strings.Builder

str = strings.ReplaceAll(str, "\r\n", "\n") // CRLF → LF
str = strings.ReplaceAll(str, "\r", "\n") // CR → LF

lines := strings.Split(str, "\n")
bytes := EncodeToRedisBitset(lines)
result.Write(bytes)

return result.String(), true
}

// EncodeToRedisBitset encodes a list of strings with integers (positions) into a Redis bitset byte array.
// The bit at position 'n' will be set to 1 if n is in the input array.
// The resulting byte slice can be stored in Redis using SET command.
func EncodeToRedisBitset(numbers []string) []byte {
if len(numbers) == 0 {
return []byte{}
}

// Find the maximum number to determine the required bit length and convert strings to numbers
maxNum := uint64(0)
var validNumbers []uint64
for _, s := range numbers {
if s == "" {
continue
}
num, err := strconv.ParseUint(s, 10, 64)
if err != nil || num < 0 || num > math.MaxUint32 {
fmt.Printf("Warning: skipping invalid number '%s': %v\n", s, err)
continue
}
validNumbers = append(validNumbers, num)
if num > maxNum {
maxNum = num
}
}

if len(validNumbers) == 0 {
return []byte{}
}

// Calculate required byte length (8 bits per byte)
byteLen := ((maxNum + 7) / 8) + 1

// Initialize byte array
bitset := make([]byte, byteLen)

// Set bits for each number
for _, num := range validNumbers {
byteIndex := num / 8
if byteIndex < byteLen {
bitIndex := uint(num % 8)
// Set the bit (big-endian bit order within byte)
bitset[byteIndex] |= 1 << (7 - bitIndex)
}
}

return bitset
}

func (BitSetConvert) Decode(str string) (string, bool) {
bitset := getBitSet([]byte(str))

var binBuilder strings.Builder
for key, value := range bitset {
if value {
binBuilder.WriteString(fmt.Sprintf("%v\n", key))
//binBuilder.WriteString(fmt.Sprintf("Bit %v = %v \n", key, value))
}
}

return binBuilder.String(), true
}

func getBitSet(redisResponse []byte) []bool {
bitset := make([]bool, len(redisResponse)*8)
for i := range redisResponse {
for j := 7; j >= 0; j-- {
bit_n := uint(i*8 + (7 - j))
bitset[bit_n] = (redisResponse[i] & (1 << uint(j))) > 0
}
}
return bitset
}
2 changes: 2 additions & 0 deletions backend/utils/convert/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var (
xmlConv XmlConvert
base64Conv Base64Convert
binaryConv BinaryConvert
bitSetConv BitSetConvert
hexConv HexConvert
gzipConv GZipConvert
deflateConv DeflateConvert
Expand All @@ -38,6 +39,7 @@ var BuildInFormatters = map[string]DataConvert{
types.FORMAT_XML: xmlConv,
types.FORMAT_HEX: hexConv,
types.FORMAT_BINARY: binaryConv,
types.FORMAT_BITSET: bitSetConv,
}

var BuildInDecoders = map[string]DataConvert{
Expand Down
1 change: 1 addition & 0 deletions frontend/src/consts/value_view_type.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const formatTypes = {
XML: 'XML',
HEX: 'Hex',
BINARY: 'Binary',
BITSET: 'BitSet',
}

/**
Expand Down