added a dictionary option

master
Jordan Orelli 11 years ago
parent 112dfe5387
commit d838df2534

@ -1,17 +1,23 @@
package main package main
import ( import (
"bufio"
"bytes"
"flag" "flag"
"fmt" "fmt"
"io"
"math/rand" "math/rand"
"os" "os"
"os/signal" "os/signal"
"strconv" "strconv"
"strings"
"syscall" "syscall"
"time" "time"
) )
var ( var (
dictionary string // pathname for a file containing dictionary words
words wordBag // index of words by length
fname string // output filename fname string // output filename
freq float64 // frequency at which lines are written freq float64 // frequency at which lines are written
ftruncate bool // whether or not to truncate file on open ftruncate bool // whether or not to truncate file on open
@ -23,6 +29,82 @@ var (
line func() string // function to generate a line line func() string // function to generate a line
) )
type wordBag map[int][]string
func (w wordBag) readAll(r io.Reader) error {
br := bufio.NewReader(r)
ReadLines:
for {
line, err := br.ReadString('\n')
switch err {
case io.EOF:
break ReadLines
case nil:
w.add(strings.TrimSpace(line))
default:
return fmt.Errorf("unable to add word to wordBag: %s", err.Error())
}
}
return nil
}
func (w wordBag) add(word string) {
if w[len(word)] == nil {
w[len(word)] = make([]string, 0, 32)
}
w[len(word)] = append(w[len(word)], word)
}
func (w wordBag) lengths() []int {
lengths := make([]int, 0, len(w))
for length, _ := range w {
lengths = append(lengths, length)
}
return lengths
}
func (w wordBag) randomWordN(n int) string {
words, ok := w[n]
if ok {
return words[rand.Intn(len(words)-1)]
}
return ""
}
func (w wordBag) randomWordBelow(n int) string {
for {
s := w.randomWordN(rand.Intn(n))
if s != "" {
return s
}
}
}
func (w wordBag) wordString(n int) string {
var (
buf bytes.Buffer
remaining int
)
for {
remaining = n - buf.Len()
switch {
case remaining < 0:
return buf.String()
case remaining < 8:
buf.WriteString(w.randomWordN(remaining))
buf.WriteRune(' ')
default:
buf.WriteString(w.randomWordBelow(remaining))
buf.WriteRune(' ')
}
}
}
// command-line length argument parsing type. Line lengths can be specified as
// either integers or strings, with strings naming known length-generating
// functions.
type lengthArg struct { type lengthArg struct {
n int n int
random bool random bool
@ -32,6 +114,7 @@ func (l *lengthArg) String() string {
return "length." return "length."
} }
// used by the flag paackge for parsing line length args
func (l *lengthArg) Set(v string) error { func (l *lengthArg) Set(v string) error {
if i, err := strconv.Atoi(v); err == nil { if i, err := strconv.Atoi(v); err == nil {
*l = lengthArg{n: i} *l = lengthArg{n: i}
@ -51,27 +134,39 @@ func (l *lengthArg) mkLineFn() (func() string, error) {
if ts == nil { if ts == nil {
ts = mkTsFn() ts = mkTsFn()
} }
if l.n == 0 {
l.n = 80
}
tsLen := len(ts()) tsLen := len(ts())
if l.random { switch {
case words != nil && l.random:
return func() string {
return words.wordString(rand.Intn(80 - tsLen))
}, nil
case words != nil:
return func() string {
return words.wordString(l.n)
}, nil
case dictionary == "" && l.random:
return func() string { return func() string {
return randomString(rand.Intn(80 - tsLen)) return randomString(rand.Intn(80 - tsLen))
}, nil }, nil
} case dictionary == "":
if l.n == 0 { if tsLen > l.n {
l.n = 80
}
if !l.random && tsLen > l.n {
return nil, fmt.Errorf("line length %d is too small for timestamps like %s", l.n, ts()) return nil, fmt.Errorf("line length %d is too small for timestamps like %s", l.n, ts())
} }
return func() string { return func() string {
return randomString(l.n - tsLen) return randomString(l.n - tsLen)
}, nil }, nil
default:
return nil, fmt.Errorf("how did I even get here?")
}
} }
// generates a pseudorandom string of length n that is composed of alphanumeric // generates a pseudorandom string of length n that is composed of alphanumeric
// characters. // characters.
func randomString(n int) string { func randomString(n int) string {
var alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" var alpha = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
buf := make([]byte, n) buf := make([]byte, n)
for i := 0; i < len(buf); i++ { for i := 0; i < len(buf); i++ {
buf[i] = alpha[rand.Intn(len(alpha)-1)] buf[i] = alpha[rand.Intn(len(alpha)-1)]
@ -181,8 +276,25 @@ func mkTsFn() func() string {
} }
} }
func readDict(path string) error {
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("unable to read dictionary file: %s", err.Error())
}
defer f.Close()
words = make(wordBag, 32)
if err := words.readAll(f); err != nil {
return fmt.Errorf("error reading dictionary file: %s", err.Error())
}
return nil
}
func flags() (err error) { func flags() (err error) {
flag.Parse() flag.Parse()
if dictionary != "" {
readDict(dictionary)
}
ts = mkTsFn() ts = mkTsFn()
line, err = lineLength.mkLineFn() line, err = lineLength.mkLineFn()
return return
@ -208,6 +320,7 @@ func init() {
flag.StringVar(&tsformat, "ts-format", "", "timestamp format") flag.StringVar(&tsformat, "ts-format", "", "timestamp format")
flag.StringVar(&pidfile, "pidfile", "", "file to which a pid is written") flag.StringVar(&pidfile, "pidfile", "", "file to which a pid is written")
flag.BoolVar(&ftruncate, "truncate", false, "truncate file on opening instead of appending") flag.BoolVar(&ftruncate, "truncate", false, "truncate file on opening instead of appending")
flag.StringVar(&dictionary, "dict", "", "dictionary of words to use for generating log data")
flag.BoolVar(&reopen, "reopen", false, "reopen file handle on every write instead of using a persistent handle") flag.BoolVar(&reopen, "reopen", false, "reopen file handle on every write instead of using a persistent handle")
flag.Float64Var(&freq, "freq", 10, "frequency in hz at which lines will be written") flag.Float64Var(&freq, "freq", 10, "frequency in hz at which lines will be written")
flag.Var(&lineLength, "line-length", "length of the lines to be generated (in bytes)") flag.Var(&lineLength, "line-length", "length of the lines to be generated (in bytes)")

Loading…
Cancel
Save