1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
module BinData
class UnRegisteredTypeError < StandardError ; end
# This registry contains a register of name -> class mappings.
#
# Numerics (integers and floating point numbers) have an endian property as
# part of their name (e.g. int32be, float_le).
#
# Classes can be looked up based on their full name or an abbreviated +name+
# with +hints+.
#
# There are two hints supported, :endian and :search_prefix.
#
# #lookup("int32", { endian: :big }) will return Int32Be.
#
# #lookup("my_type", { search_prefix: :ns }) will return NsMyType.
#
# Names are stored in under_score_style, not camelCase.
class Registry
def initialize
@registry = {}
end
def register(name, class_to_register)
return if name.nil? || class_to_register.nil?
formatted_name = underscore_name(name)
warn_if_name_is_already_registered(formatted_name, class_to_register)
@registry[formatted_name] = class_to_register
end
def unregister(name)
@registry.delete(underscore_name(name))
end
def lookup(name, hints = {})
the_class = @registry[normalize_name(name, hints)]
if the_class
the_class
elsif @registry[normalize_name(name, hints.merge(endian: :big))]
raise(UnRegisteredTypeError, "#{name}, do you need to specify endian?")
else
raise(UnRegisteredTypeError, name)
end
end
# Convert CamelCase +name+ to underscore style.
def underscore_name(name)
name.
to_s.
sub(/.*::/, "").
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
gsub(/([a-z\d])([A-Z])/, '\1_\2').
tr("-", "_").
downcase
end
#---------------
private
def normalize_name(name, hints)
name = underscore_name(name)
if !registered?(name)
search_prefix = [""].concat(Array(hints[:search_prefix]))
search_prefix.each do |prefix|
nwp = name_with_prefix(name, prefix)
if registered?(nwp)
name = nwp
break
end
nwe = name_with_endian(nwp, hints[:endian])
if registered?(nwe)
name = nwe
break
end
end
end
name
end
def name_with_prefix(name, prefix)
prefix = prefix.to_s.chomp("_")
if prefix == ""
name
else
"#{prefix}_#{name}"
end
end
def name_with_endian(name, endian)
return name if endian.nil?
suffix = (endian == :little) ? "le" : "be"
if /^u?int\d+$/ =~ name
name + suffix
else
name + "_" + suffix
end
end
def registered?(name)
register_dynamic_class(name) unless @registry.key?(name)
@registry.key?(name)
end
def register_dynamic_class(name)
if /^u?int\d+(le|be)$/ =~ name || /^s?bit\d+(le)?$/ =~ name
class_name = name.gsub(/(?:^|_)(.)/) { $1.upcase }
begin
BinData.const_get(class_name)
rescue NameError
end
end
end
def warn_if_name_is_already_registered(name, class_to_register)
prev_class = @registry[name]
if $VERBOSE && prev_class && prev_class != class_to_register
warn "warning: replacing registered class #{prev_class} " \
"with #{class_to_register}"
end
end
end
# A singleton registry of all registered classes.
RegisteredClasses = Registry.new
end
|