aboutsummaryrefslogtreecommitdiffhomepage
path: root/resources/image_cache.go
diff options
context:
space:
mode:
Diffstat (limited to 'resources/image_cache.go')
-rw-r--r--resources/image_cache.go185
1 files changed, 70 insertions, 115 deletions
diff --git a/resources/image_cache.go b/resources/image_cache.go
index f416f0230..f9770ffc1 100644
--- a/resources/image_cache.go
+++ b/resources/image_cache.go
@@ -16,12 +16,11 @@ package resources
import (
"image"
"io"
- "path/filepath"
- "strings"
- "sync"
+ "github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/resources/images"
+ "github.com/gohugoio/hugo/cache/dynacache"
"github.com/gohugoio/hugo/cache/filecache"
"github.com/gohugoio/hugo/helpers"
)
@@ -30,132 +29,88 @@ import (
type ImageCache struct {
pathSpec *helpers.PathSpec
- fileCache *filecache.Cache
-
- *imageCacheStore
-}
-
-type imageCacheStore struct {
- mu sync.RWMutex
- store map[string]*resourceAdapter
-}
-
-// WithPathSpec returns a copy of the ImageCache with the given PathSpec set.
-func (c ImageCache) WithPathSpec(ps *helpers.PathSpec) *ImageCache {
- c.pathSpec = ps
- return &c
-}
-
-func (c *ImageCache) deleteIfContains(s string) {
- c.mu.Lock()
- defer c.mu.Unlock()
- s = c.normalizeKeyBase(s)
- for k := range c.store {
- if strings.Contains(k, s) {
- delete(c.store, k)
- }
- }
-}
-
-// The cache key is a lowercase path with Unix style slashes and it always starts with
-// a leading slash.
-func (c *ImageCache) normalizeKey(key string) string {
- return "/" + c.normalizeKeyBase(key)
-}
-
-func (c *ImageCache) normalizeKeyBase(key string) string {
- return strings.Trim(strings.ToLower(filepath.ToSlash(key)), "/")
-}
-
-func (c *ImageCache) clear() {
- c.mu.Lock()
- defer c.mu.Unlock()
- c.store = make(map[string]*resourceAdapter)
+ fcache *filecache.Cache
+ mcache *dynacache.Partition[string, *resourceAdapter]
}
func (c *ImageCache) getOrCreate(
parent *imageResource, conf images.ImageConfig,
- createImage func() (*imageResource, image.Image, error)) (*resourceAdapter, error) {
+ createImage func() (*imageResource, image.Image, error),
+) (*resourceAdapter, error) {
relTarget := parent.relTargetPathFromConfig(conf)
- memKey := parent.relTargetPathForRel(relTarget.path(), false, false, false)
- memKey = c.normalizeKey(memKey)
-
- // For the file cache we want to generate and store it once if possible.
- fileKeyPath := relTarget
- if fi := parent.root.getFileInfo(); fi != nil {
- fileKeyPath.dir = filepath.ToSlash(filepath.Dir(fi.Meta().Path))
- }
- fileKey := fileKeyPath.path()
-
- // First check the in-memory store, then the disk.
- c.mu.RLock()
- cachedImage, found := c.store[memKey]
- c.mu.RUnlock()
-
- if found {
- return cachedImage, nil
- }
-
- var img *imageResource
+ relTargetPath := relTarget.TargetPath()
+ memKey := dynacache.CleanKey(relTargetPath)
+
+ v, err := c.mcache.GetOrCreate(memKey, func(key string) (*resourceAdapter, error) {
+ var img *imageResource
+
+ // These funcs are protected by a named lock.
+ // read clones the parent to its new name and copies
+ // the content to the destinations.
+ read := func(info filecache.ItemInfo, r io.ReadSeeker) error {
+ img = parent.clone(nil)
+ targetPath := img.getResourcePaths()
+ targetPath.File = relTarget.File
+ img.setTargetPath(targetPath)
+ img.setOpenSource(func() (hugio.ReadSeekCloser, error) {
+ return c.fcache.Fs.Open(info.Name)
+ })
+ img.setSourceFilenameIsHash(true)
+ img.setMediaType(conf.TargetFormat.MediaType())
+
+ if err := img.InitConfig(r); err != nil {
+ return err
+ }
+
+ return nil
+ }
- // These funcs are protected by a named lock.
- // read clones the parent to its new name and copies
- // the content to the destinations.
- read := func(info filecache.ItemInfo, r io.ReadSeeker) error {
- img = parent.clone(nil)
- rp := img.getResourcePaths()
- rp.relTargetDirFile.file = relTarget.file
- img.setSourceFilename(info.Name)
- img.setSourceFilenameIsHash(true)
- img.setMediaType(conf.TargetFormat.MediaType())
+ // create creates the image and encodes it to the cache (w).
+ create := func(info filecache.ItemInfo, w io.WriteCloser) (err error) {
+ defer w.Close()
+
+ var conv image.Image
+ img, conv, err = createImage()
+ if err != nil {
+ return
+ }
+ targetPath := img.getResourcePaths()
+ targetPath.File = relTarget.File
+ img.setTargetPath(targetPath)
+ img.setOpenSource(func() (hugio.ReadSeekCloser, error) {
+ return c.fcache.Fs.Open(info.Name)
+ })
+ return img.EncodeTo(conf, conv, w)
+ }
- return img.InitConfig(r)
- }
+ // Now look in the file cache.
- // create creates the image and encodes it to the cache (w).
- create := func(info filecache.ItemInfo, w io.WriteCloser) (err error) {
- defer w.Close()
+ // The definition of this counter is not that we have processed that amount
+ // (e.g. resized etc.), it can be fetched from file cache,
+ // but the count of processed image variations for this site.
+ c.pathSpec.ProcessingStats.Incr(&c.pathSpec.ProcessingStats.ProcessedImages)
- var conv image.Image
- img, conv, err = createImage()
+ _, err := c.fcache.ReadOrCreate(relTargetPath, read, create)
if err != nil {
- return
+ return nil, err
}
- rp := img.getResourcePaths()
- rp.relTargetDirFile.file = relTarget.file
- img.setSourceFilename(info.Name)
-
- return img.EncodeTo(conf, conv, w)
- }
-
- // Now look in the file cache.
-
- // The definition of this counter is not that we have processed that amount
- // (e.g. resized etc.), it can be fetched from file cache,
- // but the count of processed image variations for this site.
- c.pathSpec.ProcessingStats.Incr(&c.pathSpec.ProcessingStats.ProcessedImages)
-
- _, err := c.fileCache.ReadOrCreate(fileKey, read, create)
- if err != nil {
- return nil, err
- }
-
- // The file is now stored in this cache.
- img.setSourceFs(c.fileCache.Fs)
- c.mu.Lock()
- if cachedImage, found = c.store[memKey]; found {
- c.mu.Unlock()
- return cachedImage, nil
- }
+ imgAdapter := newResourceAdapter(parent.getSpec(), true, img)
- imgAdapter := newResourceAdapter(parent.getSpec(), true, img)
- c.store[memKey] = imgAdapter
- c.mu.Unlock()
+ return imgAdapter, nil
+ })
- return imgAdapter, nil
+ return v, err
}
-func newImageCache(fileCache *filecache.Cache, ps *helpers.PathSpec) *ImageCache {
- return &ImageCache{fileCache: fileCache, pathSpec: ps, imageCacheStore: &imageCacheStore{store: make(map[string]*resourceAdapter)}}
+func newImageCache(fileCache *filecache.Cache, memCache *dynacache.Cache, ps *helpers.PathSpec) *ImageCache {
+ return &ImageCache{
+ fcache: fileCache,
+ mcache: dynacache.GetOrCreatePartition[string, *resourceAdapter](
+ memCache,
+ "/imgs",
+ dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 70},
+ ),
+ pathSpec: ps,
+ }
}