2323#define RARRAY_PTR_IN_USE_FLAG FL_USER14
2424#define RARRAY_FAKEARY FL_USER19
2525
26+ /* Address of a narrowed array's packed buffer. Only the narrow decode path
27+ * may use this; everything else goes through RARRAY_CONST_PTR, which widens.
28+ * Narrow arrays always own a heap buffer: embedded arrays are never narrowed. */
29+ static inline const void *
30+ rarray_raw_ptr (VALUE ary )
31+ {
32+ RUBY_ASSERT (rb_array_stride (ary ) != RARRAY_STRIDE_VALUE );
33+ RUBY_ASSERT (!FL_TEST_RAW (ary , RARRAY_EMBED_FLAG ));
34+ return (const void * )RARRAY (ary )-> as .heap .ptr ;
35+ }
36+
37+ /* Load element i of a narrowed array as a C integer. This is the single
38+ * place that knows the width/sign encoding. It serves any strategy whose
39+ * unboxed payload is an integer; a strategy with a different payload type
40+ * would add a sibling loader. */
41+ static inline long
42+ rarray_narrow_load_int (VALUE ary , long i )
43+ {
44+ const void * p = rarray_raw_ptr (ary );
45+ if (rb_array_stride_unsigned_p (ary )) {
46+ switch (rb_array_stride (ary )) {
47+ case RARRAY_STRIDE_W8 : return (long )((const uint8_t * )p )[i ];
48+ case RARRAY_STRIDE_W16 : return (long )((const uint16_t * )p )[i ];
49+ default : return (long )((const uint32_t * )p )[i ];
50+ }
51+ }
52+ switch (rb_array_stride (ary )) {
53+ case RARRAY_STRIDE_W8 : return ((const int8_t * )p )[i ];
54+ case RARRAY_STRIDE_W16 : return ((const int16_t * )p )[i ];
55+ default : return ((const int32_t * )p )[i ];
56+ }
57+ }
58+
59+ /* Decode element i of a narrowed array into a ::VALUE. This is where the
60+ * per-strategy knowledge lives: how to rebuild a VALUE from the payload. */
61+ static inline VALUE
62+ rarray_narrow_aref (VALUE ary , long i )
63+ {
64+ return LONG2FIX (rarray_narrow_load_int (ary , i ));
65+ }
66+
67+ /* Core's RARRAY_AREF replaces the public macro (which must stay an lvalue-
68+ * capable macro for extensions). It adds a bounds assertion, and it is
69+ * narrow-aware: an element of a narrowed array is decoded in place rather
70+ * than widening the whole array, so read-only iteration (each, map, sum,
71+ * index, ==, ...) leaves a narrowed array narrow. Never allocates, so it is
72+ * also safe where RARRAY_CONST_PTR is not (opt_aref, GC callbacks).
73+ *
74+ * Not PURE: it is still a pure function of (ary, i) in the narrow case, but
75+ * the wide case reads through RARRAY_CONST_PTR, which is not pure either. */
76+ #undef RARRAY_AREF
77+ RBIMPL_ATTR_ARTIFICIAL ()
78+ static inline VALUE
79+ RARRAY_AREF (VALUE ary , long i )
80+ {
81+ VALUE val ;
82+ RBIMPL_ASSERT_TYPE (ary , RUBY_T_ARRAY );
83+
84+ RUBY_ASSERT (i < RARRAY_LEN (ary ));
85+
86+ if (RB_UNLIKELY (rb_array_stride (ary ) != RARRAY_STRIDE_VALUE )) {
87+ return rarray_narrow_aref (ary , i );
88+ }
89+
90+ RBIMPL_WARNING_PUSH ();
91+ #if defined(__GNUC__ ) && !defined(__clang__ ) && __GNUC__ == 13
92+ RBIMPL_WARNING_IGNORED (- Warray - bounds );
93+ #endif
94+ val = RARRAY_CONST_PTR (ary )[i ];
95+ RBIMPL_WARNING_POP ();
96+ return val ;
97+ }
98+
2699/* array.c */
27100VALUE rb_ary_hash_values (long len , const VALUE * elements );
28101VALUE rb_ary_last (int , const VALUE * , VALUE );
@@ -34,6 +107,11 @@ size_t rb_ary_memsize(VALUE);
34107VALUE rb_to_array_type (VALUE obj );
35108VALUE rb_to_array (VALUE obj );
36109void rb_ary_cancel_sharing (VALUE ary );
110+ /* Pack a heap array whose elements can all be stored unboxed to the narrowest
111+ * element width that holds every value. Returns the resulting stride;
112+ * RARRAY_STRIDE_VALUE means the array was left as-is. Used by the freeze
113+ * trigger and by the compiler for frozen array literals. */
114+ int rb_ary_narrow (VALUE ary );
37115size_t rb_ary_size_as_embedded (VALUE ary );
38116void rb_ary_make_embedded (VALUE ary );
39117bool rb_ary_embeddable_p (VALUE ary );
@@ -59,7 +137,6 @@ static inline VALUE
59137rb_ary_entry_internal (VALUE ary , long offset )
60138{
61139 long len = RARRAY_LEN (ary );
62- const VALUE * ptr = RARRAY_CONST_PTR (ary );
63140 if (len == 0 ) return Qnil ;
64141 if (offset < 0 ) {
65142 offset += len ;
@@ -68,7 +145,7 @@ rb_ary_entry_internal(VALUE ary, long offset)
68145 else if (len <= offset ) {
69146 return Qnil ;
70147 }
71- return ptr [ offset ];
148+ return RARRAY_AREF ( ary , offset ); /* narrow-aware; does not allocate */
72149}
73150
74151static inline bool
@@ -135,24 +212,6 @@ ARY_SHARED_ROOT_REFCNT(VALUE ary)
135212 })
136213#endif
137214
138- #undef RARRAY_AREF
139- RBIMPL_ATTR_PURE_UNLESS_DEBUG ()
140- RBIMPL_ATTR_ARTIFICIAL ()
141- static inline VALUE
142- RARRAY_AREF (VALUE ary , long i )
143- {
144- VALUE val ;
145- RBIMPL_ASSERT_TYPE (ary , RUBY_T_ARRAY );
146-
147- RUBY_ASSERT (i < RARRAY_LEN (ary ));
148215
149- RBIMPL_WARNING_PUSH ();
150- #if defined(__GNUC__ ) && !defined(__clang__ ) && __GNUC__ == 13
151- RBIMPL_WARNING_IGNORED (- Warray - bounds );
152- #endif
153- val = RARRAY_CONST_PTR (ary )[i ];
154- RBIMPL_WARNING_POP ();
155- return val ;
156- }
157216
158217#endif /* INTERNAL_ARRAY_H */
0 commit comments