summaryrefslogtreecommitdiff
path: root/include/ruby/internal/core/rarray.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/ruby/internal/core/rarray.h')
-rw-r--r--include/ruby/internal/core/rarray.h341
1 files changed, 238 insertions, 103 deletions
diff --git a/include/ruby/internal/core/rarray.h b/include/ruby/internal/core/rarray.h
index 938e2dc897..90690fe794 100644
--- a/include/ruby/internal/core/rarray.h
+++ b/include/ruby/internal/core/rarray.h
@@ -17,7 +17,7 @@
* recursively included from extension libraries written in C++.
* Do not expect for instance `__VA_ARGS__` is always available.
* We assume C99 for ruby itself but we don't assume languages of
- * extension libraries. They could be written in C++98.
+ * extension libraries. They could be written in C++98.
* @brief Defines struct ::RArray.
*/
#include "ruby/internal/arithmetic/long.h"
@@ -29,29 +29,27 @@
#include "ruby/internal/core/rbasic.h"
#include "ruby/internal/dllexport.h"
#include "ruby/internal/fl_type.h"
-#include "ruby/internal/rgengc.h"
+#include "ruby/internal/gc.h"
#include "ruby/internal/stdbool.h"
#include "ruby/internal/value.h"
#include "ruby/internal/value_type.h"
#include "ruby/assert.h"
-#ifndef USE_TRANSIENT_HEAP
-# define USE_TRANSIENT_HEAP 1
-#endif
-
+/**
+ * Convenient casting macro.
+ *
+ * @param obj An object, which is in fact an ::RArray.
+ * @return The passed object casted to ::RArray.
+ */
#define RARRAY(obj) RBIMPL_CAST((struct RArray *)(obj))
+/** @cond INTERNAL_MACRO */
#define RARRAY_EMBED_FLAG RARRAY_EMBED_FLAG
#define RARRAY_EMBED_LEN_MASK RARRAY_EMBED_LEN_MASK
#define RARRAY_EMBED_LEN_MAX RARRAY_EMBED_LEN_MAX
#define RARRAY_EMBED_LEN_SHIFT RARRAY_EMBED_LEN_SHIFT
-#if USE_TRANSIENT_HEAP
-# define RARRAY_TRANSIENT_FLAG RARRAY_TRANSIENT_FLAG
-#else
-# define RARRAY_TRANSIENT_FLAG 0
-#endif
-#define RARRAY_LEN rb_array_len
-#define RARRAY_CONST_PTR rb_array_const_ptr
-#define RARRAY_CONST_PTR_TRANSIENT rb_array_const_ptr_transient
+/** @endcond */
+#define RARRAY_LEN rb_array_len /**< @alias{rb_array_len} */
+#define RARRAY_CONST_PTR rb_array_const_ptr /**< @alias{rb_array_const_ptr} */
/** @cond INTERNAL_MACRO */
#if defined(__fcc__) || defined(__fcc_version) || \
@@ -64,33 +62,102 @@
#define RARRAY_EMBED_LEN RARRAY_EMBED_LEN
#define RARRAY_LENINT RARRAY_LENINT
-#define RARRAY_TRANSIENT_P RARRAY_TRANSIENT_P
#define RARRAY_ASET RARRAY_ASET
#define RARRAY_PTR RARRAY_PTR
/** @endcond */
+/**
+ * @private
+ *
+ * Bits that you can set to ::RBasic::flags.
+ *
+ * @warning These enums are not the only bits we use for arrays.
+ *
+ * @internal
+ *
+ * Unlike strings, flag usages for arrays are scattered across the entire
+ * source codes. @shyouhei doesn't know the complete list. But what is listed
+ * here is at least incomplete.
+ */
enum ruby_rarray_flags {
+ /**
+ * This flag has something to do with memory footprint. If the array is
+ * "small" enough, ruby tries to be creative to abuse padding bits of
+ * struct ::RArray for storing its contents. This flag denotes that
+ * situation.
+ *
+ * @warning This bit has to be considered read-only. Setting/clearing
+ * this bit without corresponding fix up must cause immediate
+ * SEGV. Also, internal structures of an array change
+ * dynamically and transparently throughout of its lifetime.
+ * Don't assume it being persistent.
+ *
+ * @internal
+ *
+ * 3rd parties must not be aware that there even is more than one way to
+ * store array elements. It was a bad idea to expose this to them.
+ */
RARRAY_EMBED_FLAG = RUBY_FL_USER1,
+
/* RUBY_FL_USER2 is for ELTS_SHARED */
- RARRAY_EMBED_LEN_MASK = RUBY_FL_USER4 | RUBY_FL_USER3
-#if USE_TRANSIENT_HEAP
- ,
- RARRAY_TRANSIENT_FLAG = RUBY_FL_USER13
-#endif
+
+ /**
+ * When an array employs embedded strategy (see ::RARRAY_EMBED_FLAG), these
+ * bits are used to store the number of elements actually filled into
+ * ::RArray::ary.
+ *
+ * @internal
+ *
+ * 3rd parties must not be aware that there even is more than one way to
+ * store array elements. It was a bad idea to expose this to them.
+ */
+ RARRAY_EMBED_LEN_MASK = RUBY_FL_USER9 | RUBY_FL_USER8 | RUBY_FL_USER7 | RUBY_FL_USER6 |
+ RUBY_FL_USER5 | RUBY_FL_USER4 | RUBY_FL_USER3
};
+/**
+ * This is an enum because GDB wants it (rather than a macro). People need not
+ * bother.
+ */
enum ruby_rarray_consts {
- RARRAY_EMBED_LEN_SHIFT = RUBY_FL_USHIFT + 3,
- RARRAY_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(VALUE)
+ /** Where ::RARRAY_EMBED_LEN_MASK resides. */
+ RARRAY_EMBED_LEN_SHIFT = RUBY_FL_USHIFT + 3
};
+/** Ruby's array. */
struct RArray {
+
+ /** Basic part, including flags and class. */
struct RBasic basic;
+
+ /** Array's specific fields. */
union {
+
+ /**
+ * Arrays that use separated memory region for elements use this
+ * pattern.
+ */
struct {
+
+ /** Number of elements of the array. */
long len;
+
+ /** Auxiliary info. */
union {
+
+ /**
+ * Capacity of `*ptr`. A continuous memory region of at least
+ * `capa` elements is expected to exist at `*ptr`. This can be
+ * bigger than `len`.
+ */
long capa;
+
+ /**
+ * Parent of the array. Nowadays arrays can share their
+ * backend memory regions each other, constructing gigantic
+ * nest of objects. This situation is called "shared", and
+ * this is the field to control such properties.
+ */
#if defined(__clang__) /* <- clang++ is sane */ || \
!defined(__cplusplus) /* <- C99 is sane */ || \
(__cplusplus > 199711L) /* <- C++11 is sane */
@@ -98,22 +165,72 @@ struct RArray {
#endif
VALUE shared_root;
} aux;
+
+ /**
+ * Pointer to the C array that holds the elements of the array. In
+ * the old days each array had dedicated memory regions. That is
+ * no longer true today, but there still are arrays of such
+ * properties. This field could be used to point such things.
+ */
const VALUE *ptr;
} heap;
- const VALUE ary[RARRAY_EMBED_LEN_MAX];
+
+ /**
+ * Embedded elements. When an array is short enough, it uses this area
+ * to store its elements. In this case the length is encoded into the
+ * flags.
+ */
+ /* This is a length 1 array because:
+ * 1. GCC has a bug that does not optimize C flexible array members
+ * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452)
+ * 2. Zero length arrays are not supported by all compilers
+ */
+ const VALUE ary[1];
} as;
};
RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * @private
+ *
+ * Declares a section of code where raw pointers are used. This is an
+ * implementation detail of #RARRAY_PTR_USE. People don't use it directly.
+ *
+ * @param[in] ary An object of ::RArray.
+ * @return `ary`'s backend C array.
+ */
VALUE *rb_ary_ptr_use_start(VALUE ary);
+
+/**
+ * @private
+ *
+ * Declares an end of a section formerly started by rb_ary_ptr_use_start().
+ * This is an implementation detail of #RARRAY_PTR_USE. People don't use it
+ * directly.
+ *
+ * @param[in] a An object of ::RArray.
+ */
void rb_ary_ptr_use_end(VALUE a);
-#if USE_TRANSIENT_HEAP
-void rb_ary_detransient(VALUE a);
-#endif
+
RBIMPL_SYMBOL_EXPORT_END()
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Queries the length of the array.
+ *
+ * @param[in] ary Array in question.
+ * @return Its number of elements.
+ * @pre `ary` must be an instance of ::RArray, and must has its
+ * ::RARRAY_EMBED_FLAG flag set.
+ *
+ * @internal
+ *
+ * This was a macro before. It was inevitable to be public, since macros are
+ * global constructs. But should it be forever? Now that it is a function,
+ * @shyouhei thinks it could just be eliminated, hidden into implementation
+ * details.
+ */
static inline long
RARRAY_EMBED_LEN(VALUE ary)
{
@@ -127,6 +244,13 @@ RARRAY_EMBED_LEN(VALUE ary)
}
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
+/**
+ * Queries the length of the array.
+ *
+ * @param[in] a Array in question.
+ * @return Its number of elements.
+ * @pre `a` must be an instance of ::RArray.
+ */
static inline long
rb_array_len(VALUE a)
{
@@ -141,6 +265,18 @@ rb_array_len(VALUE a)
}
RBIMPL_ATTR_ARTIFICIAL()
+/**
+ * Identical to rb_array_len(), except it differs for the return type.
+ *
+ * @param[in] ary Array in question.
+ * @exception rb_eRangeError Too long.
+ * @return Its number of elements.
+ * @pre `ary` must be an instance of ::RArray.
+ *
+ * @internal
+ *
+ * This API seems redundant but has actual usages.
+ */
static inline int
RARRAY_LENINT(VALUE ary)
{
@@ -148,23 +284,17 @@ RARRAY_LENINT(VALUE ary)
}
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
-RBIMPL_ATTR_ARTIFICIAL()
-static inline bool
-RARRAY_TRANSIENT_P(VALUE ary)
-{
- RBIMPL_ASSERT_TYPE(ary, RUBY_T_ARRAY);
-
-#if USE_TRANSIENT_HEAP
- return RB_FL_ANY_RAW(ary, RARRAY_TRANSIENT_FLAG);
-#else
- return false;
-#endif
-}
-
-RBIMPL_ATTR_PURE_UNLESS_DEBUG()
-/* internal function. do not use this function */
+/**
+ * @private
+ *
+ * This is an implementation detail of RARRAY_PTR(). People do not use it
+ * directly.
+ *
+ * @param[in] a An object of ::RArray.
+ * @return Its backend storage.
+ */
static inline const VALUE *
-rb_array_const_ptr_transient(VALUE a)
+rb_array_const_ptr(VALUE a)
{
RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
@@ -176,70 +306,62 @@ rb_array_const_ptr_transient(VALUE a)
}
}
-#if ! USE_TRANSIENT_HEAP
-RBIMPL_ATTR_PURE_UNLESS_DEBUG()
-#endif
-/* internal function. do not use this function */
-static inline const VALUE *
-rb_array_const_ptr(VALUE a)
-{
- RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
-
-#if USE_TRANSIENT_HEAP
- if (RARRAY_TRANSIENT_P(a)) {
- rb_ary_detransient(a);
- }
-#endif
- return rb_array_const_ptr_transient(a);
-}
-
-/* internal function. do not use this function */
-static inline VALUE *
-rb_array_ptr_use_start(VALUE a,
- RBIMPL_ATTR_MAYBE_UNUSED()
- int allow_transient)
-{
- RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
-
-#if USE_TRANSIENT_HEAP
- if (!allow_transient) {
- if (RARRAY_TRANSIENT_P(a)) {
- rb_ary_detransient(a);
- }
- }
-#endif
-
- return rb_ary_ptr_use_start(a);
-}
-
-/* internal function. do not use this function */
-static inline void
-rb_array_ptr_use_end(VALUE a,
- RBIMPL_ATTR_MAYBE_UNUSED()
- int allow_transient)
-{
- RBIMPL_ASSERT_TYPE(a, RUBY_T_ARRAY);
- rb_ary_ptr_use_end(a);
-}
-
-#define RBIMPL_RARRAY_STMT(flag, ary, var, expr) do { \
+/**
+ * @private
+ *
+ * This is an implementation detail of #RARRAY_PTR_USE. People do not use it
+ * directly.
+ */
+#define RBIMPL_RARRAY_STMT(ary, var, expr) do { \
RBIMPL_ASSERT_TYPE((ary), RUBY_T_ARRAY); \
const VALUE rbimpl_ary = (ary); \
- VALUE *var = rb_array_ptr_use_start(rbimpl_ary, (flag)); \
+ VALUE *var = rb_ary_ptr_use_start(rbimpl_ary); \
expr; \
- rb_array_ptr_use_end(rbimpl_ary, (flag)); \
+ rb_ary_ptr_use_end(rbimpl_ary); \
} while (0)
-#define RARRAY_PTR_USE_START(a) rb_array_ptr_use_start(a, 0)
-#define RARRAY_PTR_USE_END(a) rb_array_ptr_use_end(a, 0)
-#define RARRAY_PTR_USE(ary, ptr_name, expr) \
- RBIMPL_RARRAY_STMT(0, ary, ptr_name, expr)
-
-#define RARRAY_PTR_USE_START_TRANSIENT(a) rb_array_ptr_use_start(a, 1)
-#define RARRAY_PTR_USE_END_TRANSIENT(a) rb_array_ptr_use_end(a, 1)
-#define RARRAY_PTR_USE_TRANSIENT(ary, ptr_name, expr) \
- RBIMPL_RARRAY_STMT(1, ary, ptr_name, expr)
+/**
+ * Declares a section of code where raw pointers are used. In case you need to
+ * touch the raw C array instead of polite CAPIs, then that operation shall be
+ * wrapped using this macro.
+ *
+ * ```CXX
+ * const auto ary = rb_eval_string("[...]");
+ * const auto len = RARRAY_LENINT(ary);
+ * const auto symwrite = rb_intern("write");
+ *
+ * RARRAY_PTR_USE(ary, ptr, {
+ * rb_funcallv(rb_stdout, symwrite, len, ptr);
+ * });
+ * ```
+ *
+ * @param ary An object of ::RArray.
+ * @param ptr_name A variable name which points the C array in `expr`.
+ * @param expr The expression that touches `ptr_name`.
+ *
+ * @internal
+ *
+ * For historical reasons use of this macro is not enforced. There are
+ * extension libraries in the wild which call RARRAY_PTR() without it. We want
+ * them use it... Maybe some transition path can be implemented later.
+ */
+#define RARRAY_PTR_USE(ary, ptr_name, expr) \
+ RBIMPL_RARRAY_STMT(ary, ptr_name, expr)
+/**
+ * Wild use of a C pointer. This function accesses the backend storage
+ * directly. This is slower than #RARRAY_PTR_USE. It exercises
+ * extra manoeuvres to protect our generational GC. Use of this function is
+ * considered archaic. Use a modern way instead.
+ *
+ * @param[in] ary An object of ::RArray.
+ * @return The backend C array.
+ *
+ * @internal
+ *
+ * That said... there are extension libraries in the wild who uses it. We
+ * cannot but continue supporting.
+ */
static inline VALUE *
RARRAY_PTR(VALUE ary)
{
@@ -249,14 +371,27 @@ RARRAY_PTR(VALUE ary)
return RBIMPL_CAST((VALUE *)RARRAY_CONST_PTR(tmp));
}
+/**
+ * Assigns an object in an array.
+ *
+ * @param[out] ary Destination array object.
+ * @param[in] i Index of `ary`.
+ * @param[in] v Arbitrary ruby object.
+ * @pre `ary` must be an instance of ::RArray.
+ * @pre `ary`'s length must be longer than or equal to `i`.
+ * @pre `i` must be greater than or equal to zero.
+ * @post `ary`'s `i`th element is set to `v`.
+ */
static inline void
RARRAY_ASET(VALUE ary, long i, VALUE v)
{
- RARRAY_PTR_USE_TRANSIENT(ary, ptr,
+ RARRAY_PTR_USE(ary, ptr,
RB_OBJ_WRITE(ary, &ptr[i], v));
}
-/*
+/**
+ * @deprecated
+ *
* :FIXME: we want to convert RARRAY_AREF into an inline function (to add rooms
* for more sanity checks). However there were situations where the address of
* this macro is taken i.e. &RARRAY_AREF(...). They cannot be possible if this
@@ -265,6 +400,6 @@ RARRAY_ASET(VALUE ary, long i, VALUE v)
* remains as it is due to that. If we could warn such usages we can set a
* transition path, but currently no way is found to do so.
*/
-#define RARRAY_AREF(a, i) RARRAY_CONST_PTR_TRANSIENT(a)[i]
+#define RARRAY_AREF(a, i) RARRAY_CONST_PTR(a)[i]
#endif /* RBIMPL_RARRAY_H */