From d07de367da430b8f7fe1c9562a30ea6326d80b56 Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Sun, 5 Jun 2022 10:42:19 -0700 Subject: [PATCH] chore: finish up docstrings Closes: #55 Signed-off-by: Tim Bray --- doc.go | 10 ++++++---- flattener.go | 12 ++++++------ matcher.go | 5 ----- quamina.go | 13 +++++++++---- stats.go | 35 ++++++++++++++++++++++++++++++----- 5 files changed, 51 insertions(+), 24 deletions(-) diff --git a/doc.go b/doc.go index fd905cd..fde24d8 100644 --- a/doc.go +++ b/doc.go @@ -1,7 +1,9 @@ -// Package quamina suports adding Patterns to Matchers and then -// presenting Events to the matcher, which will report which of -// the Patterns matched it. Patterns and Events are both represented +// Package quamina instances support adding Patterns and then +// presenting Events, generating a report of which Patterns +// match the Event. Patterns and Events are both represented // as JSON objects, although there is a provided Flattener interface // by which structured objects in formats other than JSON can be -// matched by quamina. +// matched by quamina. Quamina instances match Events quickly and +// with a latency that is not strongly affected by the number of +// Patterns which have been added. package quamina diff --git a/flattener.go b/flattener.go index 5243e3f..3698f70 100644 --- a/flattener.go +++ b/flattener.go @@ -13,8 +13,8 @@ package quamina // "f", "33" // "f", "\"x\"" // -// Let's call the first column, eg "d" and "e\ne1", the pathSegments. For each -// step i the pathSegments, e.g. "d" and "e1", the Flattener should call +// Let's call the first column, eg "d" and "e\ne1", the path. For each +// step i the path, e.g. "d" and "e1", the Flattener should call // NameTracker.IsNameUsed(step) and if that comes back negative, not include any // paths which don't contain that step. So in the example above, if // nameTracker.IsNameUsed() only came back true for "a" and "f", then the output @@ -53,10 +53,10 @@ type ArrayPos struct { Pos int32 } -// Field represents a pathname/value combination, the data item which is matched against Patterns by the -// MatchesForEvent API. -// Path is \n-separated path from the event root to this field value. -// Val is the value - note no information as to type. +// Field represents a pathname/value combination, one of the data items which is matched +// against Patterns by the MatchesForEvent API. +// Path is the \n-separated path from the event root to this field value. +// Val is the value, a []byte forming a textual representation of the type // ArrayTrail, for each array in the Path, identifies the array and the index in it. type Field struct { Path []byte diff --git a/matcher.go b/matcher.go index 1101697..5dca6bb 100644 --- a/matcher.go +++ b/matcher.go @@ -1,10 +1,5 @@ package quamina -// X is used in the AddPattern and MatchesForEvent APIs to identify the patterns that are added to -// a Quamina instance and are reported by that instance as matching an event. Commonly, X is a string -// used to name the event. -type X any - type matcher interface { addPattern(x X, pat string) error matchesForFields(fields []Field) ([]X, error) diff --git a/quamina.go b/quamina.go index aff71f4..56ee6b8 100644 --- a/quamina.go +++ b/quamina.go @@ -92,7 +92,7 @@ func WithPatternStorage(ps LivePatternsState) Option { } } -// New returns a new Quamina instance. Consult the API's beginning with “New” for the options +// New returns a new Quamina instance. Consult the APIs beginning with “With” for the options // that may be used to configure the new instance. func New(opts ...Option) (*Quamina, error) { var q Quamina @@ -117,17 +117,22 @@ func (q *Quamina) Copy() *Quamina { return &Quamina{matcher: q.matcher, flattener: q.flattener.Copy()} } +// X is used in the AddPattern and MatchesForEvent APIs to identify the patterns that are added to +// a Quamina instance and are reported by that instance as matching an event. Commonly, X is a string +// used to name the event. +type X any + // AddPattern - adds a pattern, identified by the x argument, to a Quamina instance. // patternJSON is a JSON object. error is returned in the case that the PatternJSON is invalid JSON or // has a leaf which is not provided as an array. AddPattern is single-threaded; if it is invoked concurrently // from multiple goroutines (in instances created using the Copy method) calls will block until any other -// call in progress succeeds. +// AddPattern call in progress succeeds. func (q *Quamina) AddPattern(x X, patternJSON string) error { return q.matcher.addPattern(x, patternJSON) } -// DeletePattern removes pattnerns identified with the by the x argument from the Quamina insance; the effect -// is that return values from future calls to MatchesForEvent will not include this x value. +// DeletePattern removes pattnerns identified by the x argument from the Quamina insance; the effect +// is that return values from future calls to MatchesForEvent will not include this x value. func (q *Quamina) DeletePatterns(x X) error { return q.matcher.deletePatterns(x) } diff --git a/stats.go b/stats.go index 02219a9..10ee6be 100644 --- a/stats.go +++ b/stats.go @@ -7,15 +7,20 @@ type stats struct { fmCount int fmTblCount int fmEntries int + fmMax int fmVisited map[*fieldMatcher]bool vmCount int vmVisited map[*valueMatcher]bool stCount int + stTblCount int stEntries int + stMax int stVisited map[any]bool siCount int } +// matcherStats gathers statistics about the size of a coreMatcher, including the average and max fanout sizes of +// the transition tables, returning this information in string form func matcherStats(m *coreMatcher) string { s := stats{ fmVisited: make(map[*fieldMatcher]bool), @@ -25,10 +30,13 @@ func matcherStats(m *coreMatcher) string { fmStats(m.start().state, &s) avgFmSize := fmt.Sprintf("%.3f", float64(s.fmEntries)/float64(s.fmTblCount)) avgStSize := "n/a" - if s.stCount > 0 { - avgStSize = fmt.Sprintf("%.3f", float64(s.stEntries)/float64(s.stCount)) + if s.stTblCount > 0 { + avgStSize = fmt.Sprintf("%.3f", float64(s.stEntries)/float64(s.stTblCount)) } - return fmt.Sprintf("Field matchers: %d (avg size %s), Value matchers: %d, SmallTables %d (avg size %s), singletons %d", s.fmCount, avgFmSize, s.vmCount, s.stCount, avgStSize, s.siCount) + fmPart := fmt.Sprintf("Field matchers: %d (avg size %s, max %d), ", s.fmCount, avgFmSize, s.fmMax) + vmPart := fmt.Sprintf("Value matchers: %d, ", s.vmCount) + stPart := fmt.Sprintf("SmallTables %d (avg size %s, max %d), singletons %d", s.stCount, avgStSize, s.stMax, s.siCount) + return fmPart + vmPart + stPart } func fmStats(m *fieldMatcher, s *stats) { @@ -39,6 +47,9 @@ func fmStats(m *fieldMatcher, s *stats) { s.fmCount++ tSize := len(m.fields().transitions) if tSize > 0 { + if tSize > s.fmMax { + s.fmMax = tSize + } s.fmTblCount++ s.fmEntries += tSize } @@ -72,7 +83,14 @@ func dfaStats(t *smallTable[*dfaStep], s *stats) { } s.stVisited[t] = true s.stCount++ - s.stEntries += len(t.ceilings) + tSize := len(t.ceilings) + if tSize > 1 { + if tSize > s.stMax { + s.stMax = tSize + } + s.stTblCount++ + s.stEntries += len(t.ceilings) + } for _, step := range t.steps { if step != nil { if step.fieldTransitions != nil { @@ -91,7 +109,14 @@ func nfaStats(t *smallTable[*nfaStepList], s *stats) { } s.stVisited[t] = true s.stCount++ - s.stEntries += len(t.ceilings) + tSize := len(t.ceilings) + if tSize > 1 { + if tSize > s.stMax { + s.stMax = tSize + } + s.stTblCount++ + s.stEntries += len(t.ceilings) + } for _, stepList := range t.steps { if stepList == nil { continue