Skip to content

Commit

Permalink
feat: add -max-items and -max-dedupe-search command line args
Browse files Browse the repository at this point in the history
closes #35
  • Loading branch information
sentriz committed Feb 19, 2023
1 parent bfdf5f2 commit df85b70
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 56 deletions.
51 changes: 32 additions & 19 deletions cliphist.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"bytes"
"encoding/binary"
"errors"
"flag"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strconv"
Expand All @@ -16,17 +16,32 @@ import (
bolt "go.etcd.io/bbolt"
)

var (
maxItems = flag.Uint64("max-items", 750, "maximum number of items to store")
maxDedupeSearch = flag.Uint64("max-dedupe-search", 20, "maximum number of last items to look through when finding duplicates")
)

func main() {
usage := fmt.Sprintf("usage: $ %s <%s>", os.Args[0], strings.Join(commandList, "|"))
if len(os.Args) < 2 {
log.Fatalln(usage)
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage:\n")
fmt.Fprintf(os.Stderr, " %s <%s>\n", os.Args[0], strings.Join(commandList, "|"))
fmt.Fprintf(os.Stderr, "options:\n")
flag.PrintDefaults()
}
flag.Parse()

if len(flag.Args()) == 0 {
flag.Usage()
os.Exit(1)
}
cmd, ok := commands[os.Args[1]]
cmd, ok := commands[flag.Args()[0]]
if !ok {
log.Fatalln(usage)
flag.Usage()
os.Exit(1)
}
if err := cmd(os.Args[2:]); err != nil {
log.Fatalf("error in %q: %v", os.Args[1], err)
if err := cmd(flag.Args()[1:]); err != nil {
fmt.Fprintf(os.Stderr, "error in %q: %v", flag.Args()[1], err)
os.Exit(1)
}
}

Expand All @@ -43,9 +58,7 @@ var commands = map[string]func(args []string) error{
}
defer db.Close()

const maxStored = 750
const maxDedupe = 20
if err := store(db, input, maxDedupe, maxStored); err != nil {
if err := store(db, input, *maxDedupeSearch, *maxItems); err != nil {
return fmt.Errorf("storing: %w", err)
}
return nil
Expand Down Expand Up @@ -110,7 +123,7 @@ var commands = map[string]func(args []string) error{
return nil
},

"wipe": func(args []string) error {
"wipe": func(_ []string) error {
db, err := initDB()
if err != nil {
return fmt.Errorf("opening db: %w", err)
Expand All @@ -131,7 +144,7 @@ func init() {
}
}

func store(db *bolt.DB, input []byte, maxDedupe, maxStored uint64) error {
func store(db *bolt.DB, input []byte, maxDedupeSearch, maxItems uint64) error {
if len(bytes.TrimSpace(input)) == 0 {
return nil
}
Expand All @@ -143,7 +156,7 @@ func store(db *bolt.DB, input []byte, maxDedupe, maxStored uint64) error {

b := tx.Bucket([]byte(bucketKey))

if err := deduplicate(b, input, maxDedupe); err != nil {
if err := deduplicate(b, input, maxDedupeSearch); err != nil {
return fmt.Errorf("deduplicating: %w", err)
}
id, err := b.NextSequence()
Expand All @@ -153,7 +166,7 @@ func store(db *bolt.DB, input []byte, maxDedupe, maxStored uint64) error {
if err := b.Put(itob(id), input); err != nil {
return fmt.Errorf("insert stdin: %w", err)
}
if err := trimLength(b, maxStored); err != nil {
if err := trimLength(b, maxItems); err != nil {
return fmt.Errorf("trimming length: %w", err)
}

Expand All @@ -166,11 +179,11 @@ func store(db *bolt.DB, input []byte, maxDedupe, maxStored uint64) error {
// trim the store's size to a number of max items. manually counting
// seen items because we can't rely on sequence numbers when items can
// be deleted when deduplicating
func trimLength(b *bolt.Bucket, maxStored uint64) error {
func trimLength(b *bolt.Bucket, maxItems uint64) error {
c := b.Cursor()
var seen uint64
for k, _ := c.Last(); k != nil; k, _ = c.Prev() {
if seen < maxStored {
if seen < maxItems {
seen++
continue
}
Expand All @@ -182,11 +195,11 @@ func trimLength(b *bolt.Bucket, maxStored uint64) error {
return nil
}

func deduplicate(b *bolt.Bucket, input []byte, maxDedupe uint64) error {
func deduplicate(b *bolt.Bucket, input []byte, maxDedupeSearch uint64) error {
c := b.Cursor()
var seen uint64
for k, v := c.Last(); k != nil; k, v = c.Prev() {
if seen > maxDedupe {
if seen > maxDedupeSearch {
break
}
if !bytes.Equal(v, input) {
Expand Down
74 changes: 37 additions & 37 deletions cliphist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,21 @@ func TestStoreLimit(t *testing.T) {

is := is.New(t)

const maxStored = 10
const maxDedupe = 100
const maxItems = 10
const maxDedupeSearch = 100
const toStore = 20

for i := uint64(0); i < toStore; i++ {
is.NoErr(store(db, itob(i+1), maxDedupe, maxStored))
is.NoErr(store(db, itob(i+1), maxDedupeSearch, maxItems))
}

var buff bytes.Buffer
is.NoErr(list(db, &buff))

items := splitn(buff.Bytes())
is.Equal(len(items), maxStored) // we threw away all but the max
is.Equal(string(items[0]), preview(toStore, itob(toStore))) // last in first out
is.Equal(string(items[len(items)-1]), preview(maxStored+1, itob(maxStored+1))) // last is middle
is.Equal(len(items), maxItems) // we threw away all but the max
is.Equal(string(items[0]), preview(toStore, itob(toStore))) // last in first out
is.Equal(string(items[len(items)-1]), preview(maxItems+1, itob(maxItems+1))) // last is middle
}

func TestDeduplicate(t *testing.T) {
Expand All @@ -61,14 +61,14 @@ func TestDeduplicate(t *testing.T) {

is := is.New(t)

const maxStored = 200
const maxDedupe = 200
const maxItems = 200
const maxDedupeSearch = 200

is.NoErr(store(db, []byte("hello"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("multiple"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("multiple"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("multiple"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("hello"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("hello"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("multiple"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("multiple"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("multiple"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("hello"), maxDedupeSearch, maxItems))

var buff bytes.Buffer
is.NoErr(list(db, &buff))
Expand All @@ -86,14 +86,14 @@ func TestDeleteQuery(t *testing.T) {

is := is.New(t)

const maxStored = 200
const maxDedupe = 200
const maxItems = 200
const maxDedupeSearch = 200

is.NoErr(store(db, []byte("aa hello 1"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("bb hello 2"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 3"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("bb hello 4"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 5"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 1"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("bb hello 2"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("aa hello 3"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("bb hello 4"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("aa hello 5"), maxDedupeSearch, maxItems))

is.NoErr(deleteQuery(db, "bb"))

Expand All @@ -114,14 +114,14 @@ func TestDelete(t *testing.T) {

is := is.New(t)

const maxStored = 200
const maxDedupe = 200
const maxItems = 200
const maxDedupeSearch = 200

is.NoErr(store(db, []byte("aa hello 1"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("bb hello 2"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 3"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("bb hello 4"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 5"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 1"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("bb hello 2"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("aa hello 3"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("bb hello 4"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("aa hello 5"), maxDedupeSearch, maxItems))

is.NoErr(delete(db, []byte("3\taa hello 3")))

Expand All @@ -144,14 +144,14 @@ func TestWipe(t *testing.T) {

is := is.New(t)

const maxStored = 200
const maxDedupe = 200
const maxItems = 200
const maxDedupeSearch = 200

is.NoErr(store(db, []byte("aa hello 1"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("bb hello 2"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 3"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("bb hello 4"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 5"), maxDedupe, maxStored))
is.NoErr(store(db, []byte("aa hello 1"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("bb hello 2"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("aa hello 3"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("bb hello 4"), maxDedupeSearch, maxItems))
is.NoErr(store(db, []byte("aa hello 5"), maxDedupeSearch, maxItems))

is.NoErr(wipe(db))

Expand All @@ -169,15 +169,15 @@ func TestBinary(t *testing.T) {

is := is.New(t)

const maxStored = 200
const maxDedupe = 200
const maxItems = 200
const maxDedupeSearch = 200

var inBuff bytes.Buffer
is.NoErr(png.Encode(&inBuff, image.NewRGBA(image.Rectangle{
Min: image.Point{0, 0},
Max: image.Point{20, 20},
})))
is.NoErr(store(db, inBuff.Bytes(), maxDedupe, maxStored))
is.NoErr(store(db, inBuff.Bytes(), maxDedupeSearch, maxItems))

var outBuff bytes.Buffer
is.NoErr(decode(db, []byte(preview(1, []byte(nil))), &outBuff))
Expand Down

0 comments on commit df85b70

Please sign in to comment.