rbs 3.8.1 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependabot.yml +1 -1
  3. data/.github/workflows/ruby.yml +26 -0
  4. data/.github/workflows/typecheck.yml +2 -0
  5. data/.github/workflows/windows.yml +15 -0
  6. data/.rubocop.yml +13 -1
  7. data/CHANGELOG.md +67 -0
  8. data/Rakefile +16 -3
  9. data/config.yml +6 -0
  10. data/core/data.rbs +1 -1
  11. data/core/enumerator.rbs +14 -2
  12. data/core/exception.rbs +1 -1
  13. data/core/gc.rbs +1 -1
  14. data/core/hash.rbs +2 -2
  15. data/core/io.rbs +7 -3
  16. data/core/kernel.rbs +4 -4
  17. data/core/method.rbs +2 -2
  18. data/core/module.rbs +4 -4
  19. data/core/object.rbs +1 -1
  20. data/core/proc.rbs +2 -2
  21. data/core/rbs/unnamed/argf.rbs +3 -3
  22. data/core/rubygems/version.rbs +2 -2
  23. data/core/string.rbs +3 -3
  24. data/core/unbound_method.rbs +1 -1
  25. data/docs/syntax.md +10 -5
  26. data/ext/rbs_extension/extconf.rb +2 -1
  27. data/ext/rbs_extension/location.c +32 -10
  28. data/ext/rbs_extension/location.h +4 -3
  29. data/ext/rbs_extension/main.c +22 -1
  30. data/ext/rbs_extension/parser.c +171 -150
  31. data/ext/rbs_extension/parserstate.c +54 -9
  32. data/ext/rbs_extension/parserstate.h +6 -4
  33. data/include/rbs/ruby_objs.h +6 -6
  34. data/include/rbs/util/rbs_constant_pool.h +219 -0
  35. data/lib/rbs/ast/declarations.rb +9 -4
  36. data/lib/rbs/ast/directives.rb +10 -0
  37. data/lib/rbs/ast/members.rb +2 -0
  38. data/lib/rbs/ast/type_param.rb +2 -12
  39. data/lib/rbs/cli/validate.rb +1 -0
  40. data/lib/rbs/cli.rb +3 -3
  41. data/lib/rbs/collection/config/lockfile_generator.rb +28 -7
  42. data/lib/rbs/collection/sources/rubygems.rb +1 -1
  43. data/lib/rbs/definition.rb +46 -31
  44. data/lib/rbs/definition_builder/ancestor_builder.rb +2 -0
  45. data/lib/rbs/definition_builder.rb +86 -30
  46. data/lib/rbs/environment.rb +33 -18
  47. data/lib/rbs/errors.rb +24 -0
  48. data/lib/rbs/locator.rb +2 -0
  49. data/lib/rbs/method_type.rb +2 -0
  50. data/lib/rbs/parser_aux.rb +38 -1
  51. data/lib/rbs/prototype/rb.rb +2 -1
  52. data/lib/rbs/prototype/rbi.rb +2 -1
  53. data/lib/rbs/prototype/runtime.rb +3 -0
  54. data/lib/rbs/subtractor.rb +3 -3
  55. data/lib/rbs/test/hook.rb +2 -2
  56. data/lib/rbs/test/type_check.rb +7 -5
  57. data/lib/rbs/types.rb +45 -10
  58. data/lib/rbs/unit_test/spy.rb +4 -2
  59. data/lib/rbs/unit_test/type_assertions.rb +17 -11
  60. data/lib/rbs/unit_test/with_aliases.rb +3 -1
  61. data/lib/rbs/validator.rb +4 -0
  62. data/lib/rbs/version.rb +1 -1
  63. data/lib/rbs/writer.rb +10 -5
  64. data/lib/rbs.rb +1 -0
  65. data/rbs.gemspec +1 -1
  66. data/sig/annotate/rdoc_source.rbs +2 -0
  67. data/sig/cli.rbs +2 -0
  68. data/sig/collection/config/lockfile_generator.rbs +1 -1
  69. data/sig/declarations.rbs +10 -3
  70. data/sig/definition.rbs +67 -14
  71. data/sig/definition_builder.rbs +17 -3
  72. data/sig/directives.rbs +17 -1
  73. data/sig/environment.rbs +2 -0
  74. data/sig/errors.rbs +19 -0
  75. data/sig/namespace.rbs +1 -1
  76. data/sig/parser.rbs +5 -1
  77. data/sig/subtractor.rbs +1 -1
  78. data/sig/test/type_check.rbs +2 -2
  79. data/sig/type_param.rbs +1 -1
  80. data/sig/typename.rbs +1 -1
  81. data/sig/types.rbs +1 -1
  82. data/sig/unit_test/spy.rbs +2 -0
  83. data/sig/unit_test/type_assertions.rbs +2 -0
  84. data/sig/validator.rbs +4 -0
  85. data/sig/writer.rbs +1 -1
  86. data/src/ruby_objs.c +12 -6
  87. data/src/util/rbs_constant_pool.c +342 -0
  88. data/stdlib/cgi/0/core.rbs +10 -0
  89. data/stdlib/ipaddr/0/ipaddr.rbs +8 -0
  90. data/stdlib/json/0/json.rbs +52 -50
  91. data/stdlib/net-http/0/net-http.rbs +3 -3
  92. data/stdlib/openssl/0/openssl.rbs +73 -73
  93. data/stdlib/resolv/0/resolv.rbs +8 -8
  94. data/stdlib/socket/0/addrinfo.rbs +1 -1
  95. data/stdlib/socket/0/unix_socket.rbs +4 -2
  96. data/stdlib/stringio/0/stringio.rbs +1 -1
  97. data/stdlib/uri/0/common.rbs +17 -0
  98. metadata +4 -7
  99. data/templates/include/rbs/constants.h.erb +0 -20
  100. data/templates/include/rbs/ruby_objs.h.erb +0 -10
  101. data/templates/src/constants.c.erb +0 -36
  102. data/templates/src/ruby_objs.c.erb +0 -27
  103. data/templates/template.rb +0 -122
data/sig/directives.rbs CHANGED
@@ -1,7 +1,7 @@
1
1
  module RBS
2
2
  module AST
3
3
  module Directives
4
- type t = Use
4
+ type t = Use | ResolveTypeNames
5
5
 
6
6
  class Base
7
7
  end
@@ -56,6 +56,22 @@ module RBS
56
56
 
57
57
  def initialize: (clauses: Array[clause], location: loc?) -> void
58
58
  end
59
+
60
+ class ResolveTypeNames < Base
61
+ # ```
62
+ # # resolve-type-names: false
63
+ # ^^^^^^^^^^^^^^^^^^ keyword
64
+ # ^ colon
65
+ # ^^^^^ value
66
+ # ```
67
+ type loc = Location[:keyword | :colon | :value, bot]
68
+
69
+ attr_reader location: loc?
70
+
71
+ attr_reader value: bool
72
+
73
+ def initialize: (value: bool, location: loc?) -> void
74
+ end
59
75
  end
60
76
  end
61
77
  end
data/sig/environment.rbs CHANGED
@@ -141,6 +141,8 @@ module RBS
141
141
  #
142
142
  def resolve_type_names: (?only: Set[AST::Declarations::t]?) -> Environment
143
143
 
144
+ def resolve_signature: (Resolver::TypeNameResolver, UseMap::Table, Array[AST::Directives::t], Array[AST::Declarations::t], ?only: Set[AST::Declarations::t]?) -> [Array[AST::Directives::t], Array[AST::Declarations::t]]
145
+
144
146
  def inspect: () -> String
145
147
 
146
148
  def buffers: () -> Array[Buffer]
data/sig/errors.rbs CHANGED
@@ -182,6 +182,25 @@ module RBS
182
182
  def location: () -> AST::Members::Mixin::loc?
183
183
  end
184
184
 
185
+ class VariableDuplicationError < DefinitionError
186
+ include DetailedMessageable
187
+
188
+ attr_reader type_name: TypeName
189
+ attr_reader variable_name: Symbol
190
+ attr_reader location: Location[untyped, untyped]?
191
+
192
+ def initialize: (type_name: TypeName, variable_name: Symbol, location: Location[untyped, untyped]?) -> void
193
+ def kind: () -> String
194
+ end
195
+
196
+ class InstanceVariableDuplicationError < VariableDuplicationError
197
+ def kind: () -> String
198
+ end
199
+
200
+ class ClassInstanceVariableDuplicationError < VariableDuplicationError
201
+ def kind: () -> String
202
+ end
203
+
185
204
  # The `alias` member declares an alias from unknown method
186
205
  #
187
206
  class UnknownMethodAliasError < DefinitionError
data/sig/namespace.rbs CHANGED
@@ -142,5 +142,5 @@ end
142
142
 
143
143
  module Kernel
144
144
  # Deprecated: Use `RBS::Namespace.parse` instead
145
- %a{steep:deprecated} def Namespace: (String) -> RBS::Namespace
145
+ %a{deprecated} def Namespace: (String) -> RBS::Namespace
146
146
  end
data/sig/parser.rbs CHANGED
@@ -68,6 +68,10 @@ module RBS
68
68
  #
69
69
  def self.parse_signature: (Buffer | String) -> [Buffer, Array[AST::Directives::t], Array[AST::Declarations::t]]
70
70
 
71
+ # Returns the magic comment from the buffer
72
+ #
73
+ def self.magic_comment: (Buffer) -> AST::Directives::ResolveTypeNames?
74
+
71
75
  # Parse whole RBS file and return result.
72
76
  #
73
77
  # ```ruby
@@ -86,7 +90,7 @@ module RBS
86
90
 
87
91
  def self._parse_method_type: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables, bool require_eof) -> MethodType?
88
92
 
89
- def self._parse_signature: (Buffer, Integer end_pos) -> [Array[AST::Directives::t], Array[AST::Declarations::t]]
93
+ def self._parse_signature: (Buffer, Integer start_pos, Integer end_pos) -> [Array[AST::Directives::t], Array[AST::Declarations::t]]
90
94
 
91
95
  def self._lex: (Buffer, Integer end_pos) -> Array[[Symbol, Location[untyped, untyped]]]
92
96
 
data/sig/subtractor.rbs CHANGED
@@ -24,7 +24,7 @@ module RBS
24
24
 
25
25
  private def mixin_exist?: (TypeName owner, AST::Members::Include | AST::Members::Extend | AST::Members::Prepend, context: Resolver::context) -> boolish
26
26
 
27
- private def filter_redundunt_access_modifiers: (Array[AST::Declarations::t | AST::Members::t]) -> Array[AST::Declarations::t | AST::Members::t]
27
+ private def filter_redundant_access_modifiers: (Array[AST::Declarations::t | AST::Members::t]) -> Array[AST::Declarations::t | AST::Members::t]
28
28
 
29
29
  private def access_modifier?: (AST::Declarations::t | AST::Members::t?) -> bool
30
30
 
@@ -7,12 +7,12 @@ module RBS
7
7
  #
8
8
  # Returns an array with detected errors.
9
9
  #
10
- def method_call: (Symbol, MethodType, CallTrace, errors: Array[Errors::t]) -> Array[Errors::t]
10
+ def method_call: (Symbol, MethodType, CallTrace, errors: Array[Errors::t], ?annotations: Array[AST::Annotation]) -> Array[Errors::t]
11
11
 
12
12
  # Test if given `value` is compatible to type
13
13
  #
14
14
  # Returns `true` if the value has the type.
15
- #
15
+ #
16
16
  def value: (untyped value, Types::t) -> bool
17
17
  end
18
18
  end
data/sig/type_param.rbs CHANGED
@@ -26,7 +26,7 @@ module RBS
26
26
 
27
27
  attr_reader default_type: Types::t?
28
28
 
29
- def initialize: (name: Symbol, variance: variance, upper_bound: Types::t?, location: loc?, ?default_type: Types::t?) -> void
29
+ def initialize: (name: Symbol, variance: variance, upper_bound: Types::t?, location: loc?, ?default_type: Types::t?, ?unchecked: bool) -> void
30
30
 
31
31
  include _ToJson
32
32
 
data/sig/typename.rbs CHANGED
@@ -75,5 +75,5 @@ end
75
75
 
76
76
  module Kernel
77
77
  # Deprecated: Use `RBS::TypeName.parse` instead
78
- %a{steep:deprecated} def TypeName: (String name) -> RBS::TypeName
78
+ %a{deprecated: Use `RBS::TypeName.parse` instead} def TypeName: (String name) -> RBS::TypeName
79
79
  end
data/sig/types.rbs CHANGED
@@ -113,7 +113,7 @@ module RBS
113
113
  class Any < Base
114
114
  @string: String?
115
115
 
116
- def todo!: () -> self
116
+ def initialize: (location: Location[bot, bot]?, ?todo: bool) -> void
117
117
  end
118
118
 
119
119
  class Nil < Base
@@ -5,6 +5,8 @@ module RBS
5
5
  | [T, S] (untyped object, Symbol method_name) { (WrapSpy[T], T) -> S } -> S
6
6
 
7
7
  class WrapSpy[T]
8
+ NO_RETURN: Object
9
+
8
10
  attr_accessor callback: ^(Test::CallTrace) -> void
9
11
 
10
12
  attr_reader object: T
@@ -104,6 +104,8 @@ module RBS
104
104
  #
105
105
  def class_class: () -> Class
106
106
 
107
+ def method_defs: (Symbol) -> Array[Definition::Method::TypeDef]
108
+
107
109
  def method_types: (Symbol) -> Array[MethodType]
108
110
 
109
111
  def allows_error: (*Exception) { () -> void } -> void
data/sig/validator.rbs CHANGED
@@ -49,6 +49,10 @@ module RBS
49
49
  #
50
50
  def validate_class_alias: (entry: Environment::ClassAliasEntry | Environment::ModuleAliasEntry) -> void
51
51
 
52
+ # Validates instance/class-instance/class variables.
53
+ #
54
+ def validate_variable: (AST::Members::Var) -> void
55
+
52
56
  private
53
57
 
54
58
  # Resolves relative type names to absolute type names in given context.
data/sig/writer.rbs CHANGED
@@ -96,7 +96,7 @@ module RBS
96
96
 
97
97
  def format_annotation: (AST::Annotation) -> String
98
98
 
99
- def write_directive: (AST::Directives::t) -> void
99
+ def write_use_directive: (AST::Directives::Use) -> void
100
100
 
101
101
  def write_annotation: (Array[AST::Annotation]) -> void
102
102
 
data/src/ruby_objs.c CHANGED
@@ -71,12 +71,13 @@ VALUE rbs_ast_decl_class_super(VALUE name, VALUE args, VALUE location) {
71
71
  );
72
72
  }
73
73
 
74
- VALUE rbs_ast_decl_class_alias(VALUE new_name, VALUE old_name, VALUE location, VALUE comment) {
74
+ VALUE rbs_ast_decl_class_alias(VALUE new_name, VALUE old_name, VALUE location, VALUE comment, VALUE annotations) {
75
75
  VALUE _init_kwargs = rb_hash_new();
76
76
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("new_name")), new_name);
77
77
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("old_name")), old_name);
78
78
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location);
79
79
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment);
80
+ rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("annotations")), annotations);
80
81
 
81
82
  return CLASS_NEW_INSTANCE(
82
83
  RBS_AST_Declarations_ClassAlias,
@@ -85,12 +86,13 @@ VALUE rbs_ast_decl_class_alias(VALUE new_name, VALUE old_name, VALUE location, V
85
86
  );
86
87
  }
87
88
 
88
- VALUE rbs_ast_decl_constant(VALUE name, VALUE type, VALUE location, VALUE comment) {
89
+ VALUE rbs_ast_decl_constant(VALUE name, VALUE type, VALUE location, VALUE comment, VALUE annotations) {
89
90
  VALUE _init_kwargs = rb_hash_new();
90
91
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("name")), name);
91
92
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type);
92
93
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location);
93
94
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment);
95
+ rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("annotations")), annotations);
94
96
 
95
97
  return CLASS_NEW_INSTANCE(
96
98
  RBS_AST_Declarations_Constant,
@@ -99,12 +101,13 @@ VALUE rbs_ast_decl_constant(VALUE name, VALUE type, VALUE location, VALUE commen
99
101
  );
100
102
  }
101
103
 
102
- VALUE rbs_ast_decl_global(VALUE name, VALUE type, VALUE location, VALUE comment) {
104
+ VALUE rbs_ast_decl_global(VALUE name, VALUE type, VALUE location, VALUE comment, VALUE annotations) {
103
105
  VALUE _init_kwargs = rb_hash_new();
104
106
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("name")), name);
105
107
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type);
106
108
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location);
107
109
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment);
110
+ rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("annotations")), annotations);
108
111
 
109
112
  return CLASS_NEW_INSTANCE(
110
113
  RBS_AST_Declarations_Global,
@@ -159,12 +162,13 @@ VALUE rbs_ast_decl_module_self(VALUE name, VALUE args, VALUE location) {
159
162
  );
160
163
  }
161
164
 
162
- VALUE rbs_ast_decl_module_alias(VALUE new_name, VALUE old_name, VALUE location, VALUE comment) {
165
+ VALUE rbs_ast_decl_module_alias(VALUE new_name, VALUE old_name, VALUE location, VALUE comment, VALUE annotations) {
163
166
  VALUE _init_kwargs = rb_hash_new();
164
167
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("new_name")), new_name);
165
168
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("old_name")), old_name);
166
169
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location);
167
170
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment);
171
+ rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("annotations")), annotations);
168
172
 
169
173
  return CLASS_NEW_INSTANCE(
170
174
  RBS_AST_Declarations_ModuleAlias,
@@ -435,12 +439,13 @@ VALUE rbs_ast_members_public(VALUE location) {
435
439
  );
436
440
  }
437
441
 
438
- VALUE rbs_ast_type_param(VALUE name, VALUE variance, VALUE upper_bound, VALUE default_type, VALUE location) {
442
+ VALUE rbs_ast_type_param(VALUE name, VALUE variance, VALUE upper_bound, VALUE default_type, VALUE unchecked, VALUE location) {
439
443
  VALUE _init_kwargs = rb_hash_new();
440
444
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("name")), name);
441
445
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("variance")), variance);
442
446
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("upper_bound")), upper_bound);
443
447
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("default_type")), default_type);
448
+ rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("unchecked")), unchecked);
444
449
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location);
445
450
 
446
451
  return CLASS_NEW_INSTANCE(
@@ -501,8 +506,9 @@ VALUE rbs_alias(VALUE name, VALUE args, VALUE location) {
501
506
  );
502
507
  }
503
508
 
504
- VALUE rbs_bases_any(VALUE location) {
509
+ VALUE rbs_bases_any(VALUE todo, VALUE location) {
505
510
  VALUE _init_kwargs = rb_hash_new();
511
+ rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("todo")), todo);
506
512
  rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location);
507
513
 
508
514
  return CLASS_NEW_INSTANCE(
@@ -0,0 +1,342 @@
1
+ #include "rbs/util/rbs_constant_pool.h"
2
+
3
+ /**
4
+ * Initialize a list of constant ids.
5
+ */
6
+ void
7
+ rbs_constant_id_list_init(rbs_constant_id_list_t *list) {
8
+ list->ids = NULL;
9
+ list->size = 0;
10
+ list->capacity = 0;
11
+ }
12
+
13
+ /**
14
+ * Initialize a list of constant ids with a given capacity.
15
+ */
16
+ void
17
+ rbs_constant_id_list_init_capacity(rbs_constant_id_list_t *list, size_t capacity) {
18
+ list->ids = calloc(capacity, sizeof(rbs_constant_id_t));
19
+ if (list->ids == NULL) abort();
20
+
21
+ list->size = 0;
22
+ list->capacity = capacity;
23
+ }
24
+
25
+ /**
26
+ * Append a constant id to a list of constant ids. Returns false if any
27
+ * potential reallocations fail.
28
+ */
29
+ bool
30
+ rbs_constant_id_list_append(rbs_constant_id_list_t *list, rbs_constant_id_t id) {
31
+ if (list->size >= list->capacity) {
32
+ list->capacity = list->capacity == 0 ? 8 : list->capacity * 2;
33
+ list->ids = (rbs_constant_id_t *) realloc(list->ids, sizeof(rbs_constant_id_t) * list->capacity);
34
+ if (list->ids == NULL) return false;
35
+ }
36
+
37
+ list->ids[list->size++] = id;
38
+ return true;
39
+ }
40
+
41
+ /**
42
+ * Insert a constant id into a list of constant ids at the specified index.
43
+ */
44
+ void
45
+ rbs_constant_id_list_insert(rbs_constant_id_list_t *list, size_t index, rbs_constant_id_t id) {
46
+ assert(index < list->capacity);
47
+ assert(list->ids[index] == RBS_CONSTANT_ID_UNSET);
48
+
49
+ list->ids[index] = id;
50
+ list->size++;
51
+ }
52
+
53
+ /**
54
+ * Checks if the current constant id list includes the given constant id.
55
+ */
56
+ bool
57
+ rbs_constant_id_list_includes(rbs_constant_id_list_t *list, rbs_constant_id_t id) {
58
+ for (size_t index = 0; index < list->size; index++) {
59
+ if (list->ids[index] == id) return true;
60
+ }
61
+ return false;
62
+ }
63
+
64
+ /**
65
+ * Free the memory associated with a list of constant ids.
66
+ */
67
+ void
68
+ rbs_constant_id_list_free(rbs_constant_id_list_t *list) {
69
+ if (list->ids != NULL) {
70
+ free(list->ids);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * A relatively simple hash function (djb2) that is used to hash strings. We are
76
+ * optimizing here for simplicity and speed.
77
+ */
78
+ static inline uint32_t
79
+ rbs_constant_pool_hash(const uint8_t *start, size_t length) {
80
+ // This is a prime number used as the initial value for the hash function.
81
+ uint32_t value = 5381;
82
+
83
+ for (size_t index = 0; index < length; index++) {
84
+ value = ((value << 5) + value) + start[index];
85
+ }
86
+
87
+ return value;
88
+ }
89
+
90
+ /**
91
+ * https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
92
+ */
93
+ static uint32_t
94
+ next_power_of_two(uint32_t v) {
95
+ // Avoid underflow in subtraction on next line.
96
+ if (v == 0) {
97
+ // 1 is the nearest power of 2 to 0 (2^0)
98
+ return 1;
99
+ }
100
+ v--;
101
+ v |= v >> 1;
102
+ v |= v >> 2;
103
+ v |= v >> 4;
104
+ v |= v >> 8;
105
+ v |= v >> 16;
106
+ v++;
107
+ return v;
108
+ }
109
+
110
+ #ifndef NDEBUG
111
+ static bool
112
+ is_power_of_two(uint32_t size) {
113
+ return (size & (size - 1)) == 0;
114
+ }
115
+ #endif
116
+
117
+ /**
118
+ * Resize a constant pool to a given capacity.
119
+ */
120
+ static inline bool
121
+ rbs_constant_pool_resize(rbs_constant_pool_t *pool) {
122
+ assert(is_power_of_two(pool->capacity));
123
+
124
+ uint32_t next_capacity = pool->capacity * 2;
125
+ if (next_capacity < pool->capacity) return false;
126
+
127
+ const uint32_t mask = next_capacity - 1;
128
+ const size_t element_size = sizeof(rbs_constant_pool_bucket_t) + sizeof(rbs_constant_t);
129
+
130
+ void *next = calloc(next_capacity, element_size);
131
+ if (next == NULL) return false;
132
+
133
+ rbs_constant_pool_bucket_t *next_buckets = next;
134
+ rbs_constant_t *next_constants = (void *)(((char *) next) + next_capacity * sizeof(rbs_constant_pool_bucket_t));
135
+
136
+ // For each bucket in the current constant pool, find the index in the
137
+ // next constant pool, and insert it.
138
+ for (uint32_t index = 0; index < pool->capacity; index++) {
139
+ rbs_constant_pool_bucket_t *bucket = &pool->buckets[index];
140
+
141
+ // If an id is set on this constant, then we know we have content here.
142
+ // In this case we need to insert it into the next constant pool.
143
+ if (bucket->id != RBS_CONSTANT_ID_UNSET) {
144
+ uint32_t next_index = bucket->hash & mask;
145
+
146
+ // This implements linear scanning to find the next available slot
147
+ // in case this index is already taken. We don't need to bother
148
+ // comparing the values since we know that the hash is unique.
149
+ while (next_buckets[next_index].id != RBS_CONSTANT_ID_UNSET) {
150
+ next_index = (next_index + 1) & mask;
151
+ }
152
+
153
+ // Here we copy over the entire bucket, which includes the id so
154
+ // that they are consistent between resizes.
155
+ next_buckets[next_index] = *bucket;
156
+ }
157
+ }
158
+
159
+ // The constants are stable with respect to hash table resizes.
160
+ memcpy(next_constants, pool->constants, pool->size * sizeof(rbs_constant_t));
161
+
162
+ // pool->constants and pool->buckets are allocated out of the same chunk
163
+ // of memory, with the buckets coming first.
164
+ free(pool->buckets);
165
+ pool->constants = next_constants;
166
+ pool->buckets = next_buckets;
167
+ pool->capacity = next_capacity;
168
+ return true;
169
+ }
170
+
171
+ // This storage is initialized by `Init_rbs_extension()` in `main.c`.
172
+ static rbs_constant_pool_t RBS_GLOBAL_CONSTANT_POOL_STORAGE = {};
173
+ rbs_constant_pool_t *RBS_GLOBAL_CONSTANT_POOL = &RBS_GLOBAL_CONSTANT_POOL_STORAGE;
174
+
175
+ /**
176
+ * Initialize a new constant pool with a given capacity.
177
+ */
178
+ bool
179
+ rbs_constant_pool_init(rbs_constant_pool_t *pool, uint32_t capacity) {
180
+ const uint32_t maximum = (~((uint32_t) 0));
181
+ if (capacity >= ((maximum / 2) + 1)) return false;
182
+
183
+ capacity = next_power_of_two(capacity);
184
+ const size_t element_size = sizeof(rbs_constant_pool_bucket_t) + sizeof(rbs_constant_t);
185
+ void *memory = calloc(capacity, element_size);
186
+ if (memory == NULL) return false;
187
+
188
+ pool->buckets = memory;
189
+ pool->constants = (void *)(((char *)memory) + capacity * sizeof(rbs_constant_pool_bucket_t));
190
+ pool->size = 0;
191
+ pool->capacity = capacity;
192
+ return true;
193
+ }
194
+
195
+ /**
196
+ * Return a pointer to the constant indicated by the given constant id.
197
+ */
198
+ rbs_constant_t *
199
+ rbs_constant_pool_id_to_constant(const rbs_constant_pool_t *pool, rbs_constant_id_t constant_id) {
200
+ assert(constant_id != RBS_CONSTANT_ID_UNSET && constant_id <= pool->size);
201
+ return &pool->constants[constant_id - 1];
202
+ }
203
+
204
+ /**
205
+ * Find a constant in a constant pool. Returns the id of the constant, or 0 if
206
+ * the constant is not found.
207
+ */
208
+ rbs_constant_id_t
209
+ rbs_constant_pool_find(const rbs_constant_pool_t *pool, const uint8_t *start, size_t length) {
210
+ assert(is_power_of_two(pool->capacity));
211
+ const uint32_t mask = pool->capacity - 1;
212
+
213
+ uint32_t hash = rbs_constant_pool_hash(start, length);
214
+ uint32_t index = hash & mask;
215
+ rbs_constant_pool_bucket_t *bucket;
216
+
217
+ while (bucket = &pool->buckets[index], bucket->id != RBS_CONSTANT_ID_UNSET) {
218
+ rbs_constant_t *constant = &pool->constants[bucket->id - 1];
219
+ if ((constant->length == length) && memcmp(constant->start, start, length) == 0) {
220
+ return bucket->id;
221
+ }
222
+
223
+ index = (index + 1) & mask;
224
+ }
225
+
226
+ return RBS_CONSTANT_ID_UNSET;
227
+ }
228
+
229
+ /**
230
+ * Insert a constant into a constant pool and return its index in the pool.
231
+ */
232
+ static inline rbs_constant_id_t
233
+ rbs_constant_pool_insert(rbs_constant_pool_t *pool, const uint8_t *start, size_t length, rbs_constant_pool_bucket_type_t type) {
234
+ if (pool->size >= (pool->capacity / 4 * 3)) {
235
+ if (!rbs_constant_pool_resize(pool)) return RBS_CONSTANT_ID_UNSET;
236
+ }
237
+
238
+ assert(is_power_of_two(pool->capacity));
239
+ const uint32_t mask = pool->capacity - 1;
240
+
241
+ uint32_t hash = rbs_constant_pool_hash(start, length);
242
+ uint32_t index = hash & mask;
243
+ rbs_constant_pool_bucket_t *bucket;
244
+
245
+ while (bucket = &pool->buckets[index], bucket->id != RBS_CONSTANT_ID_UNSET) {
246
+ // If there is a collision, then we need to check if the content is the
247
+ // same as the content we are trying to insert. If it is, then we can
248
+ // return the id of the existing constant.
249
+ rbs_constant_t *constant = &pool->constants[bucket->id - 1];
250
+
251
+ if ((constant->length == length) && memcmp(constant->start, start, length) == 0) {
252
+ // Since we have found a match, we need to check if this is
253
+ // attempting to insert a shared or an owned constant. We want to
254
+ // prefer shared constants since they don't require allocations.
255
+ if (type == RBS_CONSTANT_POOL_BUCKET_OWNED) {
256
+ // If we're attempting to insert an owned constant and we have
257
+ // an existing constant, then either way we don't want the given
258
+ // memory. Either it's duplicated with the existing constant or
259
+ // it's not necessary because we have a shared version.
260
+ free((void *) start);
261
+ } else if (bucket->type == RBS_CONSTANT_POOL_BUCKET_OWNED) {
262
+ // If we're attempting to insert a shared constant and the
263
+ // existing constant is owned, then we can free the owned
264
+ // constant and replace it with the shared constant.
265
+ free((void *) constant->start);
266
+ constant->start = start;
267
+ bucket->type = (unsigned int) (RBS_CONSTANT_POOL_BUCKET_DEFAULT & 0x3);
268
+ }
269
+
270
+ return bucket->id;
271
+ }
272
+
273
+ index = (index + 1) & mask;
274
+ }
275
+
276
+ // IDs are allocated starting at 1, since the value 0 denotes a non-existent
277
+ // constant.
278
+ uint32_t id = ++pool->size;
279
+ assert(pool->size < ((uint32_t) (1 << 30)));
280
+
281
+ *bucket = (rbs_constant_pool_bucket_t) {
282
+ .id = (unsigned int) (id & 0x3fffffff),
283
+ .type = (unsigned int) (type & 0x3),
284
+ .hash = hash
285
+ };
286
+
287
+ pool->constants[id - 1] = (rbs_constant_t) {
288
+ .start = start,
289
+ .length = length,
290
+ };
291
+
292
+ return id;
293
+ }
294
+
295
+ /**
296
+ * Insert a constant into a constant pool. Returns the id of the constant, or
297
+ * RBS_CONSTANT_ID_UNSET if any potential calls to resize fail.
298
+ */
299
+ rbs_constant_id_t
300
+ rbs_constant_pool_insert_shared(rbs_constant_pool_t *pool, const uint8_t *start, size_t length) {
301
+ return rbs_constant_pool_insert(pool, start, length, RBS_CONSTANT_POOL_BUCKET_DEFAULT);
302
+ }
303
+
304
+ /**
305
+ * Insert a constant into a constant pool from memory that is now owned by the
306
+ * constant pool. Returns the id of the constant, or RBS_CONSTANT_ID_UNSET if any
307
+ * potential calls to resize fail.
308
+ */
309
+ rbs_constant_id_t
310
+ rbs_constant_pool_insert_owned(rbs_constant_pool_t *pool, uint8_t *start, size_t length) {
311
+ return rbs_constant_pool_insert(pool, start, length, RBS_CONSTANT_POOL_BUCKET_OWNED);
312
+ }
313
+
314
+ /**
315
+ * Insert a constant into a constant pool from memory that is constant. Returns
316
+ * the id of the constant, or RBS_CONSTANT_ID_UNSET if any potential calls to
317
+ * resize fail.
318
+ */
319
+ rbs_constant_id_t
320
+ rbs_constant_pool_insert_constant(rbs_constant_pool_t *pool, const uint8_t *start, size_t length) {
321
+ return rbs_constant_pool_insert(pool, start, length, RBS_CONSTANT_POOL_BUCKET_CONSTANT);
322
+ }
323
+
324
+ /**
325
+ * Free the memory associated with a constant pool.
326
+ */
327
+ void
328
+ rbs_constant_pool_free(rbs_constant_pool_t *pool) {
329
+ // For each constant in the current constant pool, free the contents if the
330
+ // contents are owned.
331
+ for (uint32_t index = 0; index < pool->capacity; index++) {
332
+ rbs_constant_pool_bucket_t *bucket = &pool->buckets[index];
333
+
334
+ // If an id is set on this constant, then we know we have content here.
335
+ if (bucket->id != RBS_CONSTANT_ID_UNSET && bucket->type == RBS_CONSTANT_POOL_BUCKET_OWNED) {
336
+ rbs_constant_t *constant = &pool->constants[bucket->id - 1];
337
+ free((void *) constant->start);
338
+ }
339
+ }
340
+
341
+ free(pool->buckets);
342
+ }
@@ -931,6 +931,11 @@ class CGI
931
931
  #
932
932
  def escapeURIComponent: (string) -> String
933
933
 
934
+ # <!-- rdoc-file=ext/cgi/escape/escape.c -->
935
+ # Returns URL-escaped string following RFC 3986.
936
+ #
937
+ alias escape_uri_component escapeURIComponent
938
+
934
939
  # <!--
935
940
  # rdoc-file=ext/cgi/escape/escape.c
936
941
  # - CGI.unescape(string, encoding=@@accept_charset) -> string
@@ -954,6 +959,11 @@ class CGI
954
959
  # Returns URL-unescaped string following RFC 3986.
955
960
  #
956
961
  def unescapeURIComponent: (string) -> String
962
+
963
+ # <!-- rdoc-file=ext/cgi/escape/escape.c -->
964
+ # Returns URL-unescaped string following RFC 3986.
965
+ #
966
+ alias unescape_uri_component unescapeURIComponent
957
967
  end
958
968
 
959
969
  # <!-- rdoc-file=lib/cgi/core.rb -->
@@ -276,6 +276,14 @@ class IPAddr
276
276
  #
277
277
  def native: () -> IPAddr
278
278
 
279
+ # <!--
280
+ # rdoc-file=lib/ipaddr.rb
281
+ # - netmask()
282
+ # -->
283
+ # Returns the netmask in string format e.g. 255.255.0.0
284
+ #
285
+ def netmask: () -> String
286
+
279
287
  # <!--
280
288
  # rdoc-file=lib/ipaddr.rb
281
289
  # - prefix()