Skip to content
Draft
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
52 changes: 41 additions & 11 deletions lib/semantic_range.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require "semantic_range/lru_cache"
require "semantic_range/version"
require "semantic_range/pre_release"
require "semantic_range/range"
Expand Down Expand Up @@ -45,6 +46,10 @@ module SemanticRange

MAX_LENGTH = 256

VERSION_CACHE = LRUCache.new(1000)
VERSION_CACHE_LOOSE = LRUCache.new(1000)
RANGE_CACHE = LRUCache.new(256)

class InvalidIncrement < StandardError; end
class InvalidVersion < StandardError; end
class InvalidComparator < StandardError; end
Expand Down Expand Up @@ -149,11 +154,12 @@ def self.outside?(version, range, hilo, loose: false, platform: nil)
end

def self.satisfies?(version, range, loose: false, platform: nil)
if valid?(range, loose: loose)
if valid?(range, loose: loose)
return version == range
end
return false if !valid_range(range, loose: loose, platform: platform)
Range.new(range, loose: loose, platform: platform).test(version)
r = cached_range(range, loose: loose, platform: platform)
return false unless r
r.test(cached_version(version, loose: loose))
end

def self.filter(versions, range, loose: false, platform: nil)
Expand All @@ -171,17 +177,15 @@ def self.max_satisfying(versions, range, loose: false, platform: nil)
end

def self.valid_range(range, loose: false, platform: nil)
begin
r = Range.new(range, loose: loose, platform: platform).range
r = '*' if r.nil? || r.empty?
r
rescue
nil
end
r = cached_range(range, loose: loose, platform: platform)
return nil unless r
result = r.range
result = '*' if result.nil? || result.empty?
result
end

def self.compare(a, b, loose: false)
Version.new(a, loose: loose).compare(b)
cached_version(a, loose: loose).compare(cached_version(b, loose: loose))
end

def self.compare_loose(a, b)
Expand Down Expand Up @@ -274,6 +278,32 @@ def self.to_comparators(range, loose: false, platform: nil)
end
end

class << self
private

def cached_version(version, loose:)
return version if version.is_a?(Version)
cache = loose ? VERSION_CACHE_LOOSE : VERSION_CACHE
cached = cache[version]
return cached unless cached.equal?(LRUCache::NOT_FOUND)
cache[version] = Version.new(version, loose: loose)
end

def cached_range(range, loose:, platform:)
return range if range.is_a?(Range)
key = [range, loose, platform]
cached = RANGE_CACHE[key]
unless cached.equal?(LRUCache::NOT_FOUND)
return cached # may be nil for invalid ranges
end
begin
RANGE_CACHE[key] = Range.new(range, loose: loose, platform: platform)
rescue InvalidRange
RANGE_CACHE[key] = nil
end
end
end

class << self
# Support for older non-inquisitive method versions
alias_method :gt, :gt?
Expand Down
24 changes: 24 additions & 0 deletions lib/semantic_range/lru_cache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module SemanticRange
class LRUCache
NOT_FOUND = Object.new.freeze

def initialize(max_size)
@max_size = max_size
@data = {}
end

def [](key)
return NOT_FOUND unless @data.key?(key)
# Move to MRU position
value = @data.delete(key)
@data[key] = value
end

def []=(key, value)
@data.delete(key) # remove existing entry (if any) before reinserting
@data[key] = value
@data.shift if @data.size > @max_size # evict LRU entry
value
end
end
end
13 changes: 4 additions & 9 deletions lib/semantic_range/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,15 @@ def compare_pre(other)
prerelease <=> other.prerelease
end

def self.compare_identifiers(a,b)
anum = /^[0-9]+$/.match(a.to_s)
bnum = /^[0-9]+$/.match(b.to_s)

if anum && bnum
a = a.to_i
b = b.to_i
end
def self.compare_identifiers(a, b)
anum = a.is_a?(Integer)
bnum = b.is_a?(Integer)

return (anum && !bnum) ? -1 :
(bnum && !anum) ? 1 :
a < b ? -1 :
a > b ? 1 :
0;
0
end

def increment!(release, identifier)
Expand Down