options: initialize all possible options to improve object shape

Options#merge works by duping-then-filling ivars, but due to not all of them being initialized on object creation, each merge had the potential of adding more object shapes for the same class, which breaks one of the most recent ruby optimizations

this was fixed by caching all possible options names at the class level, and using that as reference in the initalize method to nilify all unreferenced options
This commit is contained in:
HoneyryderChuck 2025-07-14 11:22:38 +01:00
parent af03ddba3b
commit f64c3ab599
2 changed files with 37 additions and 2 deletions

View File

@ -15,6 +15,17 @@ module HTTPX
CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil
@options_names = []
class << self
attr_reader :options_names
def inherited(klass)
super
klass.instance_variable_set(:@options_names, @options_names.dup)
end
end
# https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
ip_address_families = begin
list = Socket.ip_address_list
@ -87,6 +98,11 @@ module HTTPX
super
end
def freeze
super
@options_names.freeze
end
def method_added(meth)
super
@ -95,6 +111,8 @@ module HTTPX
optname = Regexp.last_match(1).to_sym
attr_reader(optname)
@options_names << optname
end
end
@ -145,8 +163,12 @@ module HTTPX
# This list of options are enhanced with each loaded plugin, see the plugin docs for details.
def initialize(options = {})
defaults = DEFAULT_OPTIONS.merge(options)
defaults.each do |k, v|
next if v.nil?
if v.nil?
instance_variable_set(:"@#{k}", v)
next
end
option_method_name = :"option_#{k}"
raise Error, "unknown option: #{k}" unless respond_to?(option_method_name)
@ -154,6 +176,13 @@ module HTTPX
value = __send__(option_method_name, v)
instance_variable_set(:"@#{k}", value)
end
self.class.options_names.each do |other_ivar|
next if defaults.key?(other_ivar)
instance_variable_set(:"@#{other_ivar}", nil)
end
freeze
end
@ -306,7 +335,11 @@ module HTTPX
def to_hash
instance_variables.each_with_object({}) do |ivar, hs|
hs[ivar[1..-1].to_sym] = instance_variable_get(ivar)
val = instance_variable_get(ivar)
next if val.nil?
hs[ivar[1..-1].to_sym] = val
end
end

View File

@ -2,6 +2,8 @@ module HTTPX
class Options
# include _ToHash
attr_reader self.options_names: Array[Symbol]
BUFFER_SIZE: Integer
WINDOW_SIZE: Integer
MAX_BODY_THRESHOLD_SIZE: Integer