Skip to content

Commit

Permalink
Major Refactor
Browse files Browse the repository at this point in the history
* convert from big.Int to []byte

This is more general and also moves the problem of zero prefix handling
into the library itself where it can't be accidentally missed by a user.
See block.go for how that was handled.

* remove interfaces

This library had a number of interfaces that were there with the
intention that maybe, one day, someone might want to override certain
aspects. However that day never came and it was unnecessarily
complicated. Eventually if we want that flexibility back it can happen
under the go generics regime instead.

* harden cobra usage

This removes places where previously it was just panic'ing and uses the
facilities in cobra to ensure the right number of args are passed.

* Tests!

Turns out we never had tests for this. Now we do. Huzzah!

* Docs!

Also didn't really have godoc docs. Now we at least have some!

* Easier creation with NewIBF

This function now just takes a size and seed. This is what most people
want. For those that want control over the individual hashers
NewIBFWithHash is available.

* Removed version subcommand

Until this is wired in automatically it was going to be a nuisance.
Later if there is time we can make the build process do the needful.
  • Loading branch information
calebcase committed Dec 1, 2021
1 parent 15727a9 commit 604174c
Show file tree
Hide file tree
Showing 27 changed files with 863 additions and 644 deletions.
7 changes: 0 additions & 7 deletions cmd/cannot.go

This file was deleted.

50 changes: 24 additions & 26 deletions cmd/comm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package cmd

import (
"encoding/binary"
"encoding/json"
"fmt"
"os"

"github.com/calebcase/ibf/lib"
ibf "github.com/calebcase/ibf/lib"
"github.com/spf13/cobra"
)

Expand All @@ -17,63 +16,59 @@ func indexed(data []byte) (int64, []byte) {
var commCmd = &cobra.Command{
Use: "comm IBF1 IBF2",
Short: "Compare IBF1 and IBF2.",
Run: func(cmd *cobra.Command, args []string) {
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) (err error) {
// Load IBF1 and IBF2.
paths := args
ibfs := [2]ibf.IBFer{}
sets := [2]*ibf.IBF{}

for i, path := range paths {
if i > 1 {
break
}

file, err := os.Open(path)
cannot(err)

decoder := json.NewDecoder(file)

ibf := ibf.NewEmptyIBF()
err = decoder.Decode(ibf)
cannot(err)
ibfs[i] = ibf

file.Close()
sets[i], err = open(path)
if err != nil {
return err
}
}

// Subtract IBF2 from IBF1.
ibfs[0].Subtract(ibfs[1])
ibf := ibfs[0]
set := sets[0].Clone()
set.Subtract(sets[1])

// Produce the two-column output.
leftEmpty := true
for val, err := ibf.Pop(); err == nil; val, err = ibf.Pop() {
for val, err := set.Pop(); err == nil; val, err = set.Pop() {
if !cfg.suppressLeft {
if cfg.blockIndex >= 0 {
idx, bytes := indexed(val.Bytes())
idx, bytes := indexed(val)
fmt.Printf("%d:%s\n", idx, string(bytes))
} else {
fmt.Printf("%s\n", string(val.Bytes()))
fmt.Printf("%s\n", string(val))
}
}
}
if !cfg.suppressLeft {
leftEmpty = ibf.IsEmpty()
leftEmpty = set.IsEmpty()
}

set = sets[1].Clone()
set.Subtract(sets[0])

rightEmpty := true
ibf.Invert()
for val, err := ibf.Pop(); err == nil; val, err = ibf.Pop() {
for val, err := set.Pop(); err == nil; val, err = set.Pop() {
if !cfg.suppressRight {
if cfg.blockIndex >= 0 {
idx, bytes := indexed(val.Bytes())
idx, bytes := indexed(val)
fmt.Printf("%s%d:%s\n", cfg.columnDelimiter, idx, string(bytes))
} else {
fmt.Printf("%s%s\n", cfg.columnDelimiter, string(val.Bytes()))
fmt.Printf("%s%s\n", cfg.columnDelimiter, string(val))
}
}
}
if !cfg.suppressRight {
rightEmpty = ibf.IsEmpty()
rightEmpty = set.IsEmpty()
}

// Incomplete listing?
Expand All @@ -90,8 +85,11 @@ var commCmd = &cobra.Command{
}

fmt.Fprintf(os.Stderr, "Unable to list all elements (%s).\n", side)

os.Exit(1)
}

return nil
},
}

Expand Down
16 changes: 10 additions & 6 deletions cmd/completion.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cmd

import (
"fmt"
"errors"
"os"

"github.com/spf13/cobra"
Expand All @@ -10,16 +10,20 @@ import (
var completionCmd = &cobra.Command{
Use: "completion SHELL",
Short: "Output shell completion code for the given shell.",
Run: func(cmd *cobra.Command, args []string) {
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
shell := args[0]

switch shell {
case "bash":
err := RootCmd.GenBashCompletion(os.Stdout)
cannot(err)
default:
fmt.Fprintf(os.Stderr, "Unknown shell.\n")
return RootCmd.GenBashCompletion(os.Stdout)
case "zsh":
return RootCmd.GenZshCompletion(os.Stdout)
case "powershell":
return RootCmd.GenPowerShellCompletion(os.Stdout)
}

return errors.New("unknown shell")
},
}

Expand Down
35 changes: 11 additions & 24 deletions cmd/create.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,35 @@
package cmd

import (
"encoding/json"
"math/rand"
"os"
"strconv"

"github.com/calebcase/ibf/lib"
ibf "github.com/calebcase/ibf/lib"
"github.com/spf13/cobra"
)

var createCmd = &cobra.Command{
Use: "create PATH SIZE [SEED]",
Short: "Create a new set. Optionally specify a seed for the hash parameters.",
Run: func(cmd *cobra.Command, args []string) {
Args: cobra.RangeArgs(2, 3),
RunE: func(cmd *cobra.Command, args []string) (err error) {
var path = args[0]
var seed int64 = 0

size, err := strconv.ParseUint(args[1], 10, 64)
cannot(err)
if err != nil {
return err
}

if len(args) > 2 {
seed, err = strconv.ParseInt(args[2], 10, 64)
cannot(err)
}

file, err := os.Create(path)
cannot(err)

r := rand.New(rand.NewSource(seed))

positioners := []*ibf.Hash{
ibf.NewHash(uint64(r.Int63()), uint64(r.Int63())),
ibf.NewHash(uint64(r.Int63()), uint64(r.Int63())),
ibf.NewHash(uint64(r.Int63()), uint64(r.Int63())),
if err != nil {
return err
}
}
hasher := ibf.NewHash(uint64(r.Int63()), uint64(r.Int63()))

set := ibf.NewIBF(size, positioners, hasher)

enc := json.NewEncoder(file)
set := ibf.NewIBF(size, seed)

err = enc.Encode(&set)
cannot(err)
return create(path, set)
},
}

Expand Down
50 changes: 17 additions & 33 deletions cmd/insert.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@ package cmd
import (
"bufio"
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"os"
"strings"

"github.com/calebcase/ibf/lib"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
)

var insertCmd = &cobra.Command{
Use: "insert IBF [KEY]",
Short: "Insert the key into the set. If key isn't provided, they will be read from stdin one per line.",
Run: func(cmd *cobra.Command, args []string) {
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) (err error) {
var path = args[0]

// Should we echo our input?
Expand All @@ -32,22 +30,13 @@ var insertCmd = &cobra.Command{
}
}

file, err := os.Open(path)
cannot(err)

decoder := json.NewDecoder(file)

ibf := ibf.NewEmptyIBF()
err = decoder.Decode(ibf)
cannot(err)
file.Close()
set, err := open(path)
if err != nil {
return err
}

if len(args) == 2 {
var key = args[1]

val := new(big.Int)
val.SetBytes([]byte(key))
ibf.Insert(val)
set.Insert([]byte(args[1]))
} else {
scanner := bufio.NewScanner(os.Stdin)

Expand Down Expand Up @@ -80,34 +69,29 @@ var insertCmd = &cobra.Command{
count := -1

for scanner.Scan() {
count++

bytes := scanner.Bytes()
count += 1
val := new(big.Int)

if cfg.blockSize >= 0 && cfg.blockIndex >= 0 {
idx := make([]byte, 8)
binary.LittleEndian.PutUint64(idx, uint64(count))
bytes = append(bytes, 1)
binary.BigEndian.PutUint64(idx, uint64(count))
bytes = append(bytes, idx...)
}
val.SetBytes(bytes)
ibf.Insert(val)

set.Insert(bytes)

if echoed {
fmt.Printf("%s\n", string(bytes))
}
}

cannot(scanner.Err())
err = scanner.Err()
if err != nil {
return err
}
}

file, err = os.Create(path)
cannot(err)

encoder := json.NewEncoder(file)

err = encoder.Encode(&ibf)
cannot(err)
return create(path, set)
},
}

Expand Down
30 changes: 8 additions & 22 deletions cmd/invert.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,24 @@
package cmd

import (
"encoding/json"
"os"

"github.com/calebcase/ibf/lib"
"github.com/spf13/cobra"
)

var invertCmd = &cobra.Command{
Use: "invert IBF",
Short: "Invert the counts of the IBF (multiply by -1).",
Run: func(cmd *cobra.Command, args []string) {
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
var path = args[0]

file, err := os.Open(path)
cannot(err)

decoder := json.NewDecoder(file)

ibf := ibf.NewEmptyIBF()
err = decoder.Decode(ibf)
cannot(err)
file.Close()

ibf.Invert()

file, err = os.Create(path)
cannot(err)
set, err := open(path)
if err != nil {
return err
}

encoder := json.NewEncoder(file)
set.Invert()

err = encoder.Encode(&ibf)
cannot(err)
return create(path, set)
},
}

Expand Down
Loading

0 comments on commit 604174c

Please sign in to comment.