Skip to content

Commit

Permalink
Adds no-sort flag; minor optimisations
Browse files Browse the repository at this point in the history
  • Loading branch information
tomnomnom committed Sep 7, 2016
1 parent dd4eb1f commit 2e2114b
Show file tree
Hide file tree
Showing 7 changed files with 28,442 additions and 20 deletions.
1 change: 1 addition & 0 deletions README.mkd
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ Usage:
Options:
-u, --ungron Reverse the operation (turn assignments back into JSON)
-m, --monochrome Monochrome (don't colorize output)
--no-sort Don't sort output (faster)
--version Print version information
Exit Codes:
Expand Down
3 changes: 2 additions & 1 deletion formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ func (f monoFormatter) assignment(key string, value interface{}) string {
case nil:
valStr = "null"
}
return fmt.Sprintf("%s = %s;", key, valStr)
// concatenation has proven to be faster than fmt.Sprintf here
return key + " = " + valStr + ";"
}

// colorFormatter formats statements in color
Expand Down
28 changes: 25 additions & 3 deletions formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,45 @@ func TestPrefixHappy(t *testing.T) {
}
}

func BenchmarkMakePrefixUnquoted(b *testing.B) {
func BenchmarkFormatterMakePrefixUnquoted(b *testing.B) {
var f monoFormatter
for i := 0; i < b.N; i++ {
_, _ = f.prefix("json", "isunquoted")
}
}

func BenchmarkMakePrefixQuoted(b *testing.B) {
func BenchmarkFormatterMakePrefixQuoted(b *testing.B) {
var f monoFormatter
for i := 0; i < b.N; i++ {
_, _ = f.prefix("json", "this-is-quoted")
}
}

func BenchmarkMakePrefixInt(b *testing.B) {
func BenchmarkFormatterMakePrefixInt(b *testing.B) {
var f monoFormatter
for i := 0; i < b.N; i++ {
_, _ = f.prefix("json", 212)
}
}

func BenchmarkFormatterAssignmentString(b *testing.B) {
var f monoFormatter
for i := 0; i < b.N; i++ {
_ = f.assignment("json.foo", "bar")
}
}

func BenchmarkFormatterAssignmentMap(b *testing.B) {
var f monoFormatter
val := make(map[string]interface{})
for i := 0; i < b.N; i++ {
_ = f.assignment("json.foo", val)
}
}

func BenchmarkFormatterValueString(b *testing.B) {
var f monoFormatter
for i := 0; i < b.N; i++ {
_ = f.value("a string")
}
}
43 changes: 31 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ const (
exitJSONEncode
)

// Option bitfields
const (
optMonochrome = iota + 1
optNoSort
)

// Output colors
var (
strColor = color.New(color.FgYellow)
Expand All @@ -49,6 +55,7 @@ func init() {
h += "Options:\n"
h += " -u, --ungron Reverse the operation (turn assignments back into JSON)\n"
h += " -m, --monochrome Monochrome (don't colorize output)\n"
h += " --no-sort Don't sort output (faster)\n"
h += " --version Print version information\n\n"

h += "Exit Codes:\n"
Expand All @@ -75,14 +82,16 @@ func main() {
var (
ungronFlag bool
monochromeFlag bool
noSortFlag bool
versionFlag bool
)

flag.BoolVar(&ungronFlag, "ungron", false, "Turn statements into JSON instead")
flag.BoolVar(&ungronFlag, "u", false, "Turn statements into JSON instead")
flag.BoolVar(&monochromeFlag, "monochrome", false, "Monochrome (don't colorize output)")
flag.BoolVar(&monochromeFlag, "m", false, "Monochrome (don't colorize output)")
flag.BoolVar(&versionFlag, "version", false, "Print version information")
flag.BoolVar(&ungronFlag, "ungron", false, "")
flag.BoolVar(&ungronFlag, "u", false, "")
flag.BoolVar(&monochromeFlag, "monochrome", false, "")
flag.BoolVar(&monochromeFlag, "m", false, "")
flag.BoolVar(&noSortFlag, "no-sort", false, "")
flag.BoolVar(&versionFlag, "version", false, "")

flag.Parse()

Expand Down Expand Up @@ -113,11 +122,19 @@ func main() {
}
}

var opts int
if monochromeFlag {
opts = opts | optMonochrome
}
if noSortFlag {
opts = opts | optNoSort
}

var a actionFn = gron
if ungronFlag {
a = ungron
}
exitCode, err := a(raw, os.Stdout, monochromeFlag)
exitCode, err := a(raw, os.Stdout, opts)

if exitCode != exitOK {
fatal(exitCode, err)
Expand All @@ -126,12 +143,12 @@ func main() {
os.Exit(exitOK)
}

type actionFn func(io.Reader, io.Writer, bool) (int, error)
type actionFn func(io.Reader, io.Writer, int) (int, error)

func gron(r io.Reader, w io.Writer, monochrome bool) (int, error) {
func gron(r io.Reader, w io.Writer, opts int) (int, error) {

formatter = colorFormatter{}
if monochrome {
if opts&optMonochrome > 0 {
formatter = monoFormatter{}
}

Expand All @@ -142,7 +159,9 @@ func gron(r io.Reader, w io.Writer, monochrome bool) (int, error) {

// Go's maps do not have well-defined ordering, but we want a consistent
// output for a given input, so we must sort the statements
sort.Sort(ss)
if opts&optNoSort == 0 {
sort.Sort(ss)
}

for _, s := range ss {
fmt.Fprintln(w, s)
Expand All @@ -151,7 +170,7 @@ func gron(r io.Reader, w io.Writer, monochrome bool) (int, error) {
return exitOK, nil
}

func ungron(r io.Reader, w io.Writer, monochrome bool) (int, error) {
func ungron(r io.Reader, w io.Writer, opts int) (int, error) {
scanner := bufio.NewScanner(r)

// Make a list of statements from the input
Expand Down Expand Up @@ -186,7 +205,7 @@ func ungron(r io.Reader, w io.Writer, monochrome bool) (int, error) {
}

// If the output isn't monochrome, add color to the JSON
if !monochrome {
if opts&optMonochrome == 0 {
c, err := colorizeJSON(j)

// If we failed to colorize the JSON for whatever reason,
Expand Down
24 changes: 22 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestGron(t *testing.T) {
}

out := &bytes.Buffer{}
code, err := gron(in, out, true)
code, err := gron(in, out, optMonochrome)

if code != exitOK {
t.Errorf("want exitOK; have %d", code)
Expand Down Expand Up @@ -78,7 +78,7 @@ func TestUngron(t *testing.T) {
}

out := &bytes.Buffer{}
code, err := ungron(in, out, true)
code, err := ungron(in, out, optMonochrome)

if code != exitOK {
t.Errorf("want exitOK; have %d", code)
Expand All @@ -101,3 +101,23 @@ func TestUngron(t *testing.T) {

}
}

func BenchmarkBigJSON(b *testing.B) {
in, err := os.Open("testdata/big.json")
if err != nil {
b.Fatalf("failed to open test data file: %s", err)
}

for i := 0; i < b.N; i++ {
out := &bytes.Buffer{}
_, err = in.Seek(0, 0)
if err != nil {
b.Fatalf("failed to rewind input: %s", err)
}

_, err := gron(in, out, optMonochrome|optNoSort)
if err != nil {
b.Fatalf("failed to gron: %s", err)
}
}
}
11 changes: 9 additions & 2 deletions statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,15 @@ func (ss statements) Less(a, b int) bool {
// The statements may contain ANSI color codes. We don't
// want to sort based on the colors so we need to strip
// them out first
aStr := stripColors(ss[a])
bStr := stripColors(ss[b])
var aStr, bStr string
switch formatter.(type) {
case colorFormatter:
aStr = stripColors(ss[a])
bStr = stripColors(ss[b])
default:
aStr = ss[a]
bStr = ss[b]
}

// Find where the two strings start to differ, keeping track
// of where any numbers start so that we can compare them properly
Expand Down
Loading

0 comments on commit 2e2114b

Please sign in to comment.