Skip to content

Commit

Permalink
Add package shell.
Browse files Browse the repository at this point in the history
Handle POSIX style shell splitting and quotation.
  • Loading branch information
creachadair committed Aug 8, 2024
1 parent 9fc9991 commit 0ce8166
Show file tree
Hide file tree
Showing 5 changed files with 643 additions and 1 deletion.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BSD 3-Clause License

Copyright (C) 2016, Michael J. Fromberger
Copyright (C) 2015 and on, Michael J. Fromberger
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ This repository defines generic data structures in Go.
- [mdiff](./mdiff) supports creating textual diffs ([package docs](https://godoc.org/github.com/creachadair/mds/mdiff), [example](https://go.dev/play/p/DFL6f7WLcff))
- [mstr](./mstr) helpful functions for manipulating strings ([package docs](https://godoc.org/github.com/creachadair/mds/mstr))
- [mtest](./mtest) a support library for writing tests ([package docs](https://godoc.org/github.com/creachadair/mds/mtest))
- [shell](./shell) POSIX shell quoting and splitting ([package docs](https://godoc.org/github.com/creachadair/mds/shell))
- [value](./value) helpful functions for basic values and pointers ([package docs](https://godoc.org/github.com/creachadair/mds/value))
79 changes: 79 additions & 0 deletions shell/bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) 2015, Michael J. Fromberger

package shell_test

import (
"bytes"
"fmt"
"math"
"math/rand"
"strings"
"testing"

"github.com/creachadair/mds/shell"
)

var input string

// Generate a long random string with balanced quotations for perf testing.
func init() {
var buf bytes.Buffer

src := rand.NewSource(12345)
r := rand.New(src)

const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789 \t\n\n\n"
pick := func(f float64) byte {
pos := math.Ceil(f*float64(len(alphabet))) - 1
return alphabet[int(pos)]
}

const inputLen = 100000
var quote struct {
q byte
n int
}
for i := 0; i < inputLen; i++ {
if quote.n == 0 {
q := r.Float64()
if q < .1 {
quote.q = '"'
quote.n = r.Intn(256)
buf.WriteByte('"')
continue
} else if q < .15 {
quote.q = '\''
quote.n = r.Intn(256)
buf.WriteByte('\'')
continue
}
}
buf.WriteByte(pick(r.Float64()))
if quote.n > 0 {
quote.n--
if quote.n == 0 {
buf.WriteByte(quote.q)
}
}
}
input = buf.String()
}

func BenchmarkSplit(b *testing.B) {
var lens []int
for i := 1; i < len(input); i *= 4 {
lens = append(lens, i)
}
lens = append(lens, len(input))

b.ResetTimer()
for _, n := range lens {
b.Run(fmt.Sprintf("len_%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
shell.NewScanner(strings.NewReader(input[:n])).Each(ignore)
}
})
}
}

func ignore(string) bool { return true }
Loading

0 comments on commit 0ce8166

Please sign in to comment.