Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement defer #12587

Merged
merged 1 commit into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading