feat: add 'BitSet' viewer (#503)

This commit is contained in:
blitmaster
2025-11-27 08:43:18 +01:00
committed by GitHub
parent 551c4d3db4
commit 9a2475d608
4 changed files with 105 additions and 0 deletions
+1
View File
@@ -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"
+101
View File
@@ -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
View File
@@ -20,6 +20,7 @@ var (
xmlConv XmlConvert
base64Conv Base64Convert
binaryConv BinaryConvert
bitSetConv BitSetConvert
hexConv HexConvert
gzipConv GZipConvert
deflateConv DeflateConvert
@@ -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{
+1
View File
@@ -10,6 +10,7 @@ export const formatTypes = {
XML: 'XML',
HEX: 'Hex',
BINARY: 'Binary',
BITSET: 'BitSet',
}
/**