Class: Fontisan::Variation::Cache

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/variation/cache.rb

Overview

Caches variation calculations for performance

This class implements a caching layer for expensive variation calculations to significantly improve instance generation performance. It caches:

  • Normalized scalars per coordinate set
  • Interpolated values
  • Instance generation results
  • Region matches

Cache strategies:

  1. LRU (Least Recently Used) for memory management
  2. Coordinate-based keys for scalar caching
  3. Invalidation on font modification
  4. Optional persistent caching across sessions

Examples:

Using the cache with instance generation

cache = Fontisan::Variation::Cache.new(max_size: 100)
scalars = cache.fetch_scalars(coordinates, axes) do
  calculate_scalars(coordinates, axes)
end

Cache statistics

cache.statistics
# => { hits: 150, misses: 50, hit_rate: 0.75 }

Direct Known Subclasses

ThreadSafeCache

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_size: 1000, ttl: nil) ⇒ Cache

Initialize cache

Parameters:

  • max_size (Integer) (defaults to: 1000)

    Maximum number of entries (default: 1000)

  • ttl (Integer, nil) (defaults to: nil)

    Time-to-live in seconds (nil for no expiration)



40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/fontisan/variation/cache.rb', line 40

def initialize(max_size: 1000, ttl: nil)
  @max_size = max_size
  @ttl = ttl
  @cache = {}
  @access_times = {}
  @access_counter = 0
  @stats = {
    hits: 0,
    misses: 0,
    evictions: 0,
    invalidations: 0,
  }
end

Instance Attribute Details

#max_sizeInteger (readonly)

Returns Maximum cache size.

Returns:

  • (Integer)

    Maximum cache size



31
32
33
# File 'lib/fontisan/variation/cache.rb', line 31

def max_size
  @max_size
end

#statsHash (readonly)

Returns Cache statistics.

Returns:

  • (Hash)

    Cache statistics



34
35
36
# File 'lib/fontisan/variation/cache.rb', line 34

def stats
  @stats
end

Instance Method Details

#cached?(key) ⇒ Boolean

Check if key is cached and valid

Parameters:

  • key (String)

    Cache key

Returns:

  • (Boolean)

    True if cached and valid



121
122
123
124
125
126
# File 'lib/fontisan/variation/cache.rb', line 121

def cached?(key)
  return false unless @cache.key?(key)
  return false if expired?(key)

  true
end

#clearObject

Clear entire cache



143
144
145
146
147
# File 'lib/fontisan/variation/cache.rb', line 143

def clear
  @cache.clear
  @access_times.clear
  @stats[:invalidations] += 1
end

#empty?Boolean

Check if cache is empty

Returns:

  • (Boolean)

    True if empty



191
192
193
# File 'lib/fontisan/variation/cache.rb', line 191

def empty?
  @cache.empty?
end

#fetch(key) { ... } ⇒ Object

Generic fetch with caching

Parameters:

  • key (String)

    Cache key

Yields:

  • Block to compute value if not cached

Returns:

  • (Object)

    Cached or computed value



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/fontisan/variation/cache.rb', line 104

def fetch(key)
  if cached?(key)
    @stats[:hits] += 1
    touch(key)
    return @cache[key][:value]
  end

  @stats[:misses] += 1
  value = yield
  store(key, value)
  value
end

#fetch_instance(font_checksum, coordinates) { ... } ⇒ Hash

Fetch or compute instance generation result

Parameters:

  • font_checksum (String)

    Font identifier

  • coordinates (Hash<String, Float>)

    Instance coordinates

Yields:

  • Block to generate instance if not cached

Returns:

  • (Hash)

    Cached or generated instance tables



83
84
85
86
# File 'lib/fontisan/variation/cache.rb', line 83

def fetch_instance(font_checksum, coordinates, &)
  key = CacheKeyBuilder.instance_key(font_checksum, coordinates)
  fetch(key, &)
end

#fetch_interpolated(base_value, deltas, scalars) { ... } ⇒ Float

Fetch or compute interpolated value

Parameters:

  • base_value (Numeric)

    Base value

  • deltas (Array<Numeric>)

    Delta values

  • scalars (Array<Float>)

    Region scalars

Yields:

  • Block to calculate value if not cached

Returns:

  • (Float)

    Cached or computed value



72
73
74
75
# File 'lib/fontisan/variation/cache.rb', line 72

def fetch_interpolated(base_value, deltas, scalars, &)
  key = CacheKeyBuilder.interpolation_key(base_value, deltas, scalars)
  fetch(key, &)
end

#fetch_region_matches(coordinates, regions) { ... } ⇒ Array

Fetch or compute region matches

Parameters:

  • coordinates (Hash<String, Float>)

    Design space coordinates

  • regions (Array)

    Variation regions

Yields:

  • Block to calculate matches if not cached

Returns:

  • (Array)

    Cached or computed region matches



94
95
96
97
# File 'lib/fontisan/variation/cache.rb', line 94

def fetch_region_matches(coordinates, regions, &)
  key = CacheKeyBuilder.region_matches_key(coordinates, regions)
  fetch(key, &)
end

#fetch_scalars(coordinates, axes) { ... } ⇒ Array<Float>

Fetch or compute scalars for coordinates

Parameters:

  • coordinates (Hash<String, Float>)

    Design space coordinates

  • axes (Array)

    Variation axes

Yields:

  • Block to calculate scalars if not cached

Returns:

  • (Array<Float>)

    Cached or computed scalars



60
61
62
63
# File 'lib/fontisan/variation/cache.rb', line 60

def fetch_scalars(coordinates, axes, &)
  key = CacheKeyBuilder.scalars_key(coordinates, axes)
  fetch(key, &)
end

#full?Boolean

Check if cache is full

Returns:

  • (Boolean)

    True if at capacity



198
199
200
# File 'lib/fontisan/variation/cache.rb', line 198

def full?
  @cache.size >= @max_size
end

#invalidate(key) ⇒ Object

Invalidate specific key

Parameters:

  • key (String)

    Cache key to invalidate



152
153
154
155
156
# File 'lib/fontisan/variation/cache.rb', line 152

def invalidate(key)
  @cache.delete(key)
  @access_times.delete(key)
  @stats[:invalidations] += 1
end

#invalidate_matching(pattern) ⇒ Object

Invalidate keys matching pattern

Parameters:

  • pattern (Regexp)

    Pattern to match keys



161
162
163
164
# File 'lib/fontisan/variation/cache.rb', line 161

def invalidate_matching(pattern)
  keys = @cache.keys.select { |k| k.match?(pattern) }
  keys.each { |k| invalidate(k) }
end

#sizeInteger

Get cache size

Returns:

  • (Integer)

    Number of cached entries



184
185
186
# File 'lib/fontisan/variation/cache.rb', line 184

def size
  @cache.size
end

#statisticsHash

Get cache statistics

Returns:

  • (Hash)

    Statistics including hit rate



169
170
171
172
173
174
175
176
177
178
179
# File 'lib/fontisan/variation/cache.rb', line 169

def statistics
  total = @stats[:hits] + @stats[:misses]
  hit_rate = total.zero? ? 0.0 : @stats[:hits].to_f / total

  @stats.merge(
    total_requests: total,
    hit_rate: hit_rate,
    size: @cache.size,
    max_size: @max_size,
  )
end

#store(key, value) ⇒ Object

Store value in cache

Parameters:

  • key (String)

    Cache key

  • value (Object)

    Value to store



132
133
134
135
136
137
138
139
140
# File 'lib/fontisan/variation/cache.rb', line 132

def store(key, value)
  evict_if_needed

  @cache[key] = {
    value: value,
    created_at: Time.now,
  }
  touch(key)
end