File: registry.rb

package info (click to toggle)
ruby-bindata 2.4.14-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 600 kB
  • sloc: ruby: 8,566; makefile: 4
file content (134 lines) | stat: -rw-r--r-- 3,432 bytes parent folder | download | duplicates (2)
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