Skip to content

Commit

Permalink
Implement defer
Browse files Browse the repository at this point in the history
Closes #8086
Closes #12589
  • Loading branch information
bep committed Jun 23, 2024
1 parent 8731d88 commit 6cd0784
Show file tree
Hide file tree
Showing 33 changed files with 1,027 additions and 142 deletions.
18 changes: 14 additions & 4 deletions cache/dynacache/dynacache.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ import (

const minMaxSize = 10

type KeyIdentity struct {
Key any
Identity identity.Identity
}

// New creates a new cache.
func New(opts Options) *Cache {
if opts.CheckInterval == 0 {
Expand All @@ -64,14 +69,14 @@ func New(opts Options) *Cache {

infol := opts.Log.InfoCommand("dynacache")

evictedIdentities := collections.NewStack[identity.Identity]()
evictedIdentities := collections.NewStack[KeyIdentity]()

onEvict := func(k, v any) {
if !opts.Watching {
return
}
identity.WalkIdentitiesShallow(v, func(level int, id identity.Identity) bool {
evictedIdentities.Push(id)
evictedIdentities.Push(KeyIdentity{Key: k, Identity: id})
return false
})
resource.MarkStale(v)
Expand Down Expand Up @@ -124,7 +129,7 @@ type Cache struct {
partitions map[string]PartitionManager

onEvict func(k, v any)
evictedIdentities *collections.Stack[identity.Identity]
evictedIdentities *collections.Stack[KeyIdentity]

opts Options
infol logg.LevelLogger
Expand All @@ -135,10 +140,15 @@ type Cache struct {
}

// DrainEvictedIdentities drains the evicted identities from the cache.
func (c *Cache) DrainEvictedIdentities() []identity.Identity {
func (c *Cache) DrainEvictedIdentities() []KeyIdentity {
return c.evictedIdentities.Drain()
}

// DrainEvictedIdentitiesMatching drains the evicted identities from the cache that match the given predicate.
func (c *Cache) DrainEvictedIdentitiesMatching(predicate func(KeyIdentity) bool) []KeyIdentity {
return c.evictedIdentities.DrainMatching(predicate)
}

// ClearMatching clears all partition for which the predicate returns true.
func (c *Cache) ClearMatching(predicatePartition func(k string, p PartitionManager) bool, predicateValue func(k, v any) bool) {
if predicatePartition == nil {
Expand Down
1 change: 0 additions & 1 deletion cache/httpcache/httpcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ func (c *Config) Compile() (ConfigCompiled, error) {
}

// PollConfig holds the configuration for polling remote resources to detect changes in watch mode.
// TODO1 make sure this enabled only in watch mode.
type PollConfig struct {
// What remote resources to apply this configuration to.
For GlobMatcher
Expand Down
13 changes: 13 additions & 0 deletions common/collections/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,16 @@ func (s *Stack[T]) Drain() []T {
s.items = nil
return items
}

func (s *Stack[T]) DrainMatching(predicate func(T) bool) []T {
s.mu.Lock()
defer s.mu.Unlock()
var items []T
for i := len(s.items) - 1; i >= 0; i-- {
if predicate(s.items[i]) {
items = append(items, s.items[i])
s.items = append(s.items[:i], s.items[i+1:]...)
}
}
return items
}
41 changes: 37 additions & 4 deletions common/herrors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ func (e *TimeoutError) Is(target error) bool {
return ok
}

// errMessage wraps an error with a message.
type errMessage struct {
msg string
err error
}

func (e *errMessage) Error() string {
return e.msg
}

func (e *errMessage) Unwrap() error {
return e.err
}

// IsFeatureNotAvailableError returns true if the given error is or contains a FeatureNotAvailableError.
func IsFeatureNotAvailableError(err error) bool {
return errors.Is(err, &FeatureNotAvailableError{})
Expand Down Expand Up @@ -121,19 +135,38 @@ func IsNotExist(err error) bool {

var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtime error: invalid memory address or nil pointer dereference`)

func ImproveIfNilPointer(inErr error) (outErr error) {
const deferredPrefix = "__hdeferred/"

var deferredStringToRemove = regexp.MustCompile(`executing "__hdeferred/.*" `)

// ImproveRenderErr improves the error message for rendering errors.
func ImproveRenderErr(inErr error) (outErr error) {
outErr = inErr
msg := improveIfNilPointerMsg(inErr)
if msg != "" {
outErr = &errMessage{msg: msg, err: outErr}
}

if strings.Contains(inErr.Error(), deferredPrefix) {
msg := deferredStringToRemove.ReplaceAllString(inErr.Error(), "executing ")
outErr = &errMessage{msg: msg, err: outErr}
}
return
}

func improveIfNilPointerMsg(inErr error) string {
m := nilPointerErrRe.FindStringSubmatch(inErr.Error())
if len(m) == 0 {
return
return ""
}
call := m[1]
field := m[2]
parts := strings.Split(call, ".")
if len(parts) < 2 {
return ""
}
receiverName := parts[len(parts)-2]
receiver := strings.Join(parts[:len(parts)-1], ".")
s := fmt.Sprintf("– %s is nil; wrap it in if or with: {{ with %s }}{{ .%s }}{{ end }}", receiverName, receiver, field)
outErr = errors.New(nilPointerErrRe.ReplaceAllString(inErr.Error(), s))
return
return nilPointerErrRe.ReplaceAllString(inErr.Error(), s)
}
41 changes: 32 additions & 9 deletions common/hugio/hasBytesWriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,35 @@ import (
"bytes"
)

// HasBytesWriter is a writer that will set Match to true if the given pattern
// is found in the stream.
// HasBytesWriter is a writer will match against a slice of patterns.
type HasBytesWriter struct {
Match bool
Pattern []byte
Patterns []*HasBytesPattern

i int
done bool
buff []byte
}

type HasBytesPattern struct {
Match bool
Pattern []byte
}

func (h *HasBytesWriter) patternLen() int {
l := 0
for _, p := range h.Patterns {
l += len(p.Pattern)
}
return l
}

func (h *HasBytesWriter) Write(p []byte) (n int, err error) {
if h.done {
return len(p), nil
}

if len(h.buff) == 0 {
h.buff = make([]byte, len(h.Pattern)*2)
h.buff = make([]byte, h.patternLen()*2)
}

for i := range p {
Expand All @@ -46,11 +57,23 @@ func (h *HasBytesWriter) Write(p []byte) (n int, err error) {
h.i = len(h.buff) / 2
}

if bytes.Contains(h.buff, h.Pattern) {
h.Match = true
h.done = true
return len(p), nil
for _, pp := range h.Patterns {
if bytes.Contains(h.buff, pp.Pattern) {
pp.Match = true
done := true
for _, ppp := range h.Patterns {
if !ppp.Match {
done = false
break
}
}
if done {
h.done = true
}
return len(p), nil
}
}

}

return len(p), nil
Expand Down
13 changes: 8 additions & 5 deletions common/hugio/hasBytesWriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ func TestHasBytesWriter(t *testing.T) {
var b bytes.Buffer

h := &HasBytesWriter{
Pattern: []byte("__foo"),
Patterns: []*HasBytesPattern{
{Pattern: []byte("__foo")},
},
}

return h, io.MultiWriter(&b, h)
}

Expand All @@ -46,19 +49,19 @@ func TestHasBytesWriter(t *testing.T) {
for i := 0; i < 22; i++ {
h, w := neww()
fmt.Fprintf(w, rndStr()+"abc __foobar"+rndStr())
c.Assert(h.Match, qt.Equals, true)
c.Assert(h.Patterns[0].Match, qt.Equals, true)

h, w = neww()
fmt.Fprintf(w, rndStr()+"abc __f")
fmt.Fprintf(w, "oo bar"+rndStr())
c.Assert(h.Match, qt.Equals, true)
c.Assert(h.Patterns[0].Match, qt.Equals, true)

h, w = neww()
fmt.Fprintf(w, rndStr()+"abc __moo bar")
c.Assert(h.Match, qt.Equals, false)
c.Assert(h.Patterns[0].Match, qt.Equals, false)
}

h, w := neww()
fmt.Fprintf(w, "__foo")
c.Assert(h.Match, qt.Equals, true)
c.Assert(h.Patterns[0].Match, qt.Equals, true)
}
20 changes: 20 additions & 0 deletions common/maps/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ func (c *Cache[K, T]) ForEeach(f func(K, T)) {
}
}

func (c *Cache[K, T]) Drain() map[K]T {
c.Lock()
m := c.m
c.m = make(map[K]T)
c.Unlock()
return m
}

func (c *Cache[K, T]) Len() int {
c.RLock()
defer c.RUnlock()
return len(c.m)
}

func (c *Cache[K, T]) Reset() {
c.Lock()
c.m = make(map[K]T)
c.Unlock()
}

// SliceCache is a simple thread safe cache backed by a map.
type SliceCache[T any] struct {
m map[string][]T
Expand Down
41 changes: 36 additions & 5 deletions common/paths/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,17 @@ func prettifyPath(in string, b filepathPathBridge) string {
return b.Join(b.Dir(in), name, "index"+ext)
}

// CommonDir returns the common directory of the given paths.
func CommonDir(path1, path2 string) string {
// CommonDirPath returns the common directory of the given paths.
func CommonDirPath(path1, path2 string) string {
if path1 == "" || path2 == "" {
return ""
}

hadLeadingSlash := strings.HasPrefix(path1, "/") || strings.HasPrefix(path2, "/")

path1 = TrimLeading(path1)
path2 = TrimLeading(path2)

p1 := strings.Split(path1, "/")
p2 := strings.Split(path2, "/")

Expand All @@ -256,7 +261,13 @@ func CommonDir(path1, path2 string) string {
}
}

return strings.Join(common, "/")
s := strings.Join(common, "/")

if hadLeadingSlash && s != "" {
s = "/" + s
}

return s
}

// Sanitize sanitizes string to be used in Hugo's file paths and URLs, allowing only
Expand Down Expand Up @@ -384,16 +395,36 @@ func PathEscape(pth string) string {

// ToSlashTrimLeading is just a filepath.ToSlash with an added / prefix trimmer.
func ToSlashTrimLeading(s string) string {
return strings.TrimPrefix(filepath.ToSlash(s), "/")
return TrimLeading(filepath.ToSlash(s))
}

// TrimLeading trims the leading slash from the given string.
func TrimLeading(s string) string {
return strings.TrimPrefix(s, "/")
}

// ToSlashTrimTrailing is just a filepath.ToSlash with an added / suffix trimmer.
func ToSlashTrimTrailing(s string) string {
return strings.TrimSuffix(filepath.ToSlash(s), "/")
return TrimTrailing(filepath.ToSlash(s))
}

// TrimTrailing trims the trailing slash from the given string.
func TrimTrailing(s string) string {
return strings.TrimSuffix(s, "/")
}

// ToSlashTrim trims any leading and trailing slashes from the given string and converts it to a forward slash separated path.
func ToSlashTrim(s string) string {
return strings.Trim(filepath.ToSlash(s), "/")
}

// ToSlashPreserveLeading converts the path given to a forward slash separated path
// and preserves the leading slash if present trimming any trailing slash.
func ToSlashPreserveLeading(s string) string {
return "/" + strings.Trim(filepath.ToSlash(s), "/")
}

// IsSameFilePath checks if s1 and s2 are the same file path.
func IsSameFilePath(s1, s2 string) bool {
return path.Clean(ToSlashTrim(s1)) == path.Clean(ToSlashTrim(s2))
}
Loading

0 comments on commit 6cd0784

Please sign in to comment.