diff options
Diffstat (limited to 'src/string.c')
| -rw-r--r-- | src/string.c | 743 |
1 files changed, 532 insertions, 211 deletions
diff --git a/src/string.c b/src/string.c index 24e0959..d490ec3 100644 --- a/src/string.c +++ b/src/string.c @@ -16,7 +16,6 @@ #include <mruby/string.h> #include <mruby/numeric.h> #include <mruby/internal.h> -#include <mruby/presym.h> #include <string.h> typedef struct mrb_shared_string { @@ -40,7 +39,7 @@ const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static void str_check_length(mrb_state *mrb, mrb_int len) { - if (len < 0) { + if (len < 0 || len == MRB_INT_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative (or overflowed) string size"); } #if MRB_STR_LENGTH_MAX != 0 @@ -50,6 +49,20 @@ str_check_length(mrb_state *mrb, mrb_int len) #endif } +mrb_bool +mrb_strcasecmp_p(const char *s1, mrb_int len1, const char *s2, mrb_int len2) +{ + if (len1 != len2) return FALSE; + + const char *e1 = s1 + len1; + while (s1 < e1) { + if (*s1 != *s2 && TOUPPER(*s1) != TOUPPER(*s2)) return FALSE; + s1++; + s2++; + } + return TRUE; +} + static struct RString* str_init_normal_capa(mrb_state *mrb, struct RString *s, const char *p, mrb_int len, mrb_int capa) @@ -152,6 +165,14 @@ str_new(mrb_state *mrb, const char *p, mrb_int len) return str_init_normal(mrb, mrb_obj_alloc_string(mrb), p, len); } +/* + * @param mrb The mruby state. + * @param capa The desired capacity of the new string. + * @return A new mruby string with the specified capacity. + * + * Creates a new mruby string with a given initial capacity. + * The string is initially empty. + */ MRB_API mrb_value mrb_str_new_capa(mrb_state *mrb, mrb_int capa) { @@ -181,12 +202,29 @@ resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) } } +/* + * @param mrb The mruby state. + * @param p A pointer to the C string to copy. + * @param len The length of the C string. + * @return A new mruby string containing the copied C string. + * + * Creates a new mruby string from a C string and a specified length. + * If `p` is NULL, an empty string is created. + */ MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, mrb_int len) { return mrb_obj_value(str_new(mrb, p, len)); } +/* + * @param mrb The mruby state. + * @param p A pointer to the null-terminated C string to copy. + * @return A new mruby string containing the copied C string. + * + * Creates a new mruby string from a null-terminated C string. + * If `p` is NULL, an empty string is created. + */ MRB_API mrb_value mrb_str_new_cstr(mrb_state *mrb, const char *p) { @@ -205,6 +243,16 @@ mrb_str_new_cstr(mrb_state *mrb, const char *p) return mrb_obj_value(s); } +/* + * @param mrb The mruby state. + * @param p A pointer to the static C string. + * @param len The length of the static C string. + * @return A new mruby string referencing the static C string. + * + * Creates a new mruby string that directly references a static C string. + * The C string is not copied and must remain valid for the lifetime of the mruby string. + * This is typically used for string literals. + */ MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, mrb_int len) { @@ -281,6 +329,39 @@ mrb_gc_free_str(mrb_state *mrb, struct RString *str) #define MASK01 0x01010101ul #endif +/* + * Encode a Unicode codepoint to UTF-8 bytes. + * buf must have at least 4 bytes of space. + * Returns the number of bytes written (1-4), or 0 for invalid codepoint. + */ +mrb_int +mrb_utf8_to_buf(char *buf, uint32_t cp) +{ + if (cp < 0x80) { + buf[0] = (char)cp; + return 1; + } + else if (cp < 0x800) { + buf[0] = (char)(0xC0 | (cp >> 6)); + buf[1] = (char)(0x80 | (cp & 0x3F)); + return 2; + } + else if (cp < 0x10000) { + buf[0] = (char)(0xE0 | (cp >> 12)); + buf[1] = (char)(0x80 | ((cp >> 6) & 0x3F)); + buf[2] = (char)(0x80 | (cp & 0x3F)); + return 3; + } + else if (cp <= 0x10FFFF) { + buf[0] = (char)(0xF0 | (cp >> 18)); + buf[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); + buf[2] = (char)(0x80 | ((cp >> 6) & 0x3F)); + buf[3] = (char)(0x80 | (cp & 0x3F)); + return 4; + } + return 0; /* invalid codepoint */ +} + #ifdef MRB_UTF8_STRING #define NOASCII(c) ((c) & 0x80) @@ -407,12 +488,13 @@ mrb_utf8len(const char* p, const char* e) # define popcount(x) __builtin_popcountl(x) # endif #else +#define POPC_SHIFT (8 * sizeof(bitint) - 8) static inline uint32_t popcount(bitint x) { x = (x & (MASK01*0x55)) + ((x >> 1) & (MASK01*0x55)); x = (x & (MASK01*0x33)) + ((x >> 2) & (MASK01*0x33)); x = (x & (MASK01*0x0F)) + ((x >> 4) & (MASK01*0x0F)); - return (x * MASK01) >> 56; + return (uint32_t)((x * MASK01) >> POPC_SHIFT); } #endif @@ -693,6 +775,17 @@ str_share(mrb_state *mrb, struct RString *orig, struct RString *s) } } +/* + * @param mrb The mruby state. + * @param str The original mruby string. + * @param beg The starting byte offset of the substring. + * @param len The length in bytes of the substring. + * @return A new mruby string representing the byte subsequence. + * + * Creates a new mruby string that is a subsequence of an existing string, + * based on byte offsets and length. This function may share the underlying + * buffer with the original string if possible. + */ mrb_value mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { @@ -746,6 +839,17 @@ str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) str_subseq(mrb, str, beg, len) : mrb_nil_value(); } +/* + * @param mrb The mruby state. + * @param str The mruby string to search in. + * @param sptr A pointer to the C string to search for. + * @param slen The length of the C string to search for. + * @param offset The byte offset at which to start the search. + * @return The byte offset of the first occurrence of the substring, or -1 if not found. + * + * Finds the first occurrence of a C string within an mruby string, starting from a given offset. + * The search is performed on a byte-by-byte basis. + */ MRB_API mrb_int mrb_str_index(mrb_state *mrb, mrb_value str, const char *sptr, mrb_int slen, mrb_int offset) { @@ -900,6 +1004,15 @@ mrb_locale_from_utf8(const char *utf8, int len) } #endif +/* + * @param mrb The mruby state. + * @param s The RString structure to modify. + * + * Prepares a string for modification. If the string is shared or not extensible, + * it will be unshared or converted to a normal string. This version preserves + * the ASCII/single-byte nature of the string if it was already set. + * Raises an error if the string is frozen. + */ MRB_API void mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s) { @@ -907,6 +1020,15 @@ mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s) str_unshare_buffer(mrb, s); } +/* + * @param mrb The mruby state. + * @param s The RString structure to modify. + * + * Prepares a string for modification. Similar to `mrb_str_modify_keep_ascii`, + * but also unsets the single-byte flag, assuming the modification might + * introduce multi-byte characters. + * Raises an error if the string is frozen. + */ MRB_API void mrb_str_modify(mrb_state *mrb, struct RString *s) { @@ -914,15 +1036,26 @@ mrb_str_modify(mrb_state *mrb, struct RString *s) RSTR_UNSET_SINGLE_BYTE_FLAG(s); } +/* + * @param mrb The mruby state. + * @param str The mruby string to resize. + * @param len The new desired length of the string. + * @return The resized mruby string. + * + * Resizes an mruby string to a new length. + * If the new length is shorter, the string is truncated. + * If the new length is longer, the string is extended, and the new portion's + * content is undefined (it might be null bytes or garbage). + * The string is modified in place. + */ MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) { - mrb_int slen; struct RString *s = mrb_str_ptr(str); str_check_length(mrb, len); mrb_str_modify(mrb, s); - slen = RSTR_LEN(s); + mrb_int slen = RSTR_LEN(s); if (len != slen) { if (slen < len || slen - len > 256) { resize_capa(mrb, s, len); @@ -933,6 +1066,21 @@ mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) return str; } +/* + * @param mrb The mruby state. + * @param str0 The mruby string to convert. + * @return A pointer to a null-terminated C string. + * + * Converts an mruby string to a null-terminated C string. + * This function may allocate a new C string if the mruby string + * contains null bytes or is not already null-terminated. + * The caller is responsible for managing the memory of the returned C string + * if it's different from the string's internal buffer. + * Raises E_ARGUMENT_ERROR if the string contains a null byte. + * Note: This function creates a *new* RString object to hold the C-string version if modification is needed. + * It's generally recommended to use RSTRING_PTR and RSTRING_LEN for direct access + * and ensure null termination manually if needed, or use mrb_string_cstr for a (potentially new) null-terminated string. + */ MRB_API char* mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) { @@ -945,6 +1093,14 @@ mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) return RSTR_PTR(s); } +/* + * @param mrb The mruby state. + * @param self The mruby string to append to (modified in place). + * @param other The mruby value to append (will be converted to a string). + * + * Concatenates the string representation of `other` to `self`. + * `self` is modified in place. + */ MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) { @@ -952,6 +1108,14 @@ mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) mrb_str_cat_str(mrb, self, other); } +/* + * @param mrb The mruby state. + * @param a The first mruby string. + * @param b The second mruby string. + * @return A new mruby string that is the concatenation of `a` and `b`. + * + * Creates a new mruby string by concatenating two existing mruby strings. + */ MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) { @@ -977,8 +1141,8 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) * call-seq: * str + other_str -> new_str * - * Concatenation---Returns a new <code>String</code> containing - * <i>other_str</i> concatenated to <i>str</i>. + * Concatenation---Returns a new `String` containing + * `other_str` concatenated to `str`. * * "Hello from " + self.to_s #=> "Hello from main" */ @@ -1017,7 +1181,7 @@ mrb_str_bytesize(mrb_state *mrb, mrb_value self) * call-seq: * str * integer => new_str * - * Copy---Returns a new <code>String</code> containing <i>integer</i> copies of + * Copy---Returns a new `String` containing `integer` copies of * the receiver. * * "Ho! " * 3 #=> "Ho! Ho! Ho! " @@ -1063,6 +1227,16 @@ mrb_str_times(mrb_state *mrb, mrb_value self) * = 0 * < -1 */ +/* + * @param mrb The mruby state. + * @param str1 The first mruby string for comparison. + * @param str2 The second mruby string for comparison (must be a string). + * @return An integer less than, equal to, or greater than zero if `str1` is less than, + * equal to, or greater than `str2`, respectively. + * + * Compares two mruby strings lexicographically. + * Assumes `str2` is already a string. For a version that checks and converts, see `mrb_str_cmp_m`. + */ MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) { @@ -1072,7 +1246,8 @@ mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) mrb_int len1 = RSTR_LEN(s1); mrb_int len2 = RSTR_LEN(s2); mrb_int len = lesser(len1, len2); - mrb_int retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len); + mrb_int retval = (len == 0) ? 0 : memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len); + if (retval == 0) { if (len1 == len2) return 0; if (len1 > len2) return 1; @@ -1081,6 +1256,7 @@ mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) if (retval > 0) return 1; return -1; } +#undef lesser /* 15.2.10.5.3 */ @@ -1088,20 +1264,19 @@ mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) * call-seq: * str <=> other_str => -1, 0, +1 * - * Comparison---Returns -1 if <i>other_str</i> is less than, 0 if - * <i>other_str</i> is equal to, and +1 if <i>other_str</i> is greater than - * <i>str</i>. If the strings are of different lengths, and the strings are + * Comparison---Returns -1 if `other_str` is less than, 0 if + * `other_str` is equal to, and +1 if `other_str` is greater than + * `str`. If the strings are of different lengths, and the strings are * equal when compared up to the shortest length, then the longer string is - * considered greater than the shorter one. If the variable <code>$=</code> is - * <code>false</code>, the comparison is based on comparing the binary values + * considered greater than the shorter one. If the variable `$=` is + * `false`, the comparison is based on comparing the binary values * of each character in the string. In older versions of Ruby, setting - * <code>$=</code> allowed case-insensitive comparisons; this is now deprecated - * in favor of using <code>String#casecmp</code>. + * `$=` allowed case-insensitive comparisons; this is now deprecated + * in favor of using `String#casecmp`. * - * <code><=></code> is the basis for the methods <code><</code>, - * <code><=</code>, <code>></code>, <code>>=</code>, and <code>between?</code>, - * included from module <code>Comparable</code>. The method - * <code>String#==</code> does not use <code>Comparable#==</code>. + * `<=>` is the basis for the methods `<`, `<=`, `>`, `>=`, and `between?`, + * included from module `Comparable`. The method `String#==` does not use + * `Comparable#==`. * * "abcdef" <=> "abcde" #=> 1 * "abcdef" <=> "abcdef" #=> 0 @@ -1130,6 +1305,15 @@ str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2) return (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), (size_t)len) == 0); } +/* + * @param mrb The mruby state. + * @param str1 The first mruby string. + * @param str2 The second mruby value to compare with. + * @return `TRUE` if `str1` and `str2` are equal strings, `FALSE` otherwise. + * + * Checks if two mruby strings are equal. + * Returns `FALSE` if `str2` is not a string. + */ MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) { @@ -1143,10 +1327,10 @@ mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) * str == obj => true or false * * Equality--- - * If <i>obj</i> is not a <code>String</code>, returns <code>false</code>. - * Otherwise, returns <code>false</code> or <code>true</code> + * If `obj` is not a `String`, returns `false`. + * Otherwise, returns `false` or `true` * - * caution:if <i>str</i> <code><=></code> <i>obj</i> returns zero. + * caution:if `str` `<=>` `obj` returns zero. */ static mrb_value mrb_str_equal_m(mrb_state *mrb, mrb_value str1) @@ -1157,6 +1341,14 @@ mrb_str_equal_m(mrb_state *mrb, mrb_value str1) } /* ---------------------------------- */ +/* + * @param mrb The mruby state. + * @param str The mruby string to duplicate. + * @return A new mruby string that is a copy of the original. + * + * Creates a new mruby string that is a duplicate of the given string. + * The new string will have its own buffer. + */ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str) { @@ -1181,32 +1373,32 @@ enum str_convert_range { }; static enum str_convert_range -str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_int *beg, mrb_int *len) +str_convert_range(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen, mrb_int *beg, mrb_int *len) { if (!mrb_undef_p(alen)) { - *beg = mrb_as_int(mrb, indx); + *beg = mrb_as_int(mrb, idx); *len = mrb_as_int(mrb, alen); return STR_CHAR_RANGE; } else { - switch (mrb_type(indx)) { + switch (mrb_type(idx)) { default: - indx = mrb_ensure_int_type(mrb, indx); + idx = mrb_ensure_int_type(mrb, idx); /* fall through */ case MRB_TT_INTEGER: - *beg = mrb_integer(indx); + *beg = mrb_integer(idx); *len = 1; return STR_CHAR_RANGE; case MRB_TT_STRING: - *beg = str_index_str(mrb, str, indx, 0); + *beg = str_index_str(mrb, str, idx, 0); if (*beg < 0) { break; } - *len = RSTRING_LEN(indx); + *len = RSTRING_LEN(idx); return STR_BYTE_RANGE_CORRECTED; case MRB_TT_RANGE: *len = RSTRING_CHAR_LEN(str); - switch (mrb_range_beg_len(mrb, indx, beg, len, *len, TRUE)) { + switch (mrb_range_beg_len(mrb, idx, beg, len, *len, TRUE)) { case MRB_RANGE_OK: return STR_CHAR_RANGE_CORRECTED; case MRB_RANGE_OUT: @@ -1219,12 +1411,25 @@ str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, return STR_OUT_OF_RANGE; } +/* + * @param mrb The mruby state. + * @param str The mruby string. + * @param idx The index or range. Can be an integer, a string, or a range. + * @param alen An optional length (if `idx` is an integer). + * @return A new mruby string (substring), or nil if out of bounds or not found. + * + * Implements string element reference (e.g., `str[idx]`, `str[idx, len]`). + * - If `idx` is an Integer, returns a substring of 1 character at that index (or `len` characters if `alen` is provided). + * - If `idx` is a String, returns that string if it's a substring of `str`. + * - If `idx` is a Range, returns the substring specified by the range. + * Character indexing is used if UTF-8 is enabled, otherwise byte indexing. + */ mrb_value -mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen) +mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen) { mrb_int beg, len; - switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) { + switch (str_convert_range(mrb, str, idx, alen, &beg, &len)) { case STR_CHAR_RANGE_CORRECTED: return str_subseq(mrb, str, beg, len); case STR_CHAR_RANGE: @@ -1232,8 +1437,8 @@ mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen) if (mrb_undef_p(alen) && !mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); return str; case STR_BYTE_RANGE_CORRECTED: - if (mrb_string_p(indx)) { - return mrb_str_dup(mrb, indx); + if (mrb_string_p(idx)) { + return mrb_str_dup(mrb, idx); } else { return mrb_str_byte_subseq(mrb, str, beg, len); @@ -1257,17 +1462,17 @@ mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen) * str.slice(range) => new_str or nil * str.slice(other_str) => new_str or nil * - * Element Reference---If passed a single <code>Integer</code>, returns the code - * of the character at that position. If passed two <code>Integer</code> + * Element Reference---If passed a single `Integer`, returns the code + * of the character at that position. If passed two `Integer` * objects, returns a substring starting at the offset given by the first, and * a length given by the second. If given a range, a substring containing * characters at offsets given by the range is returned. In all three cases, if - * an offset is negative, it is counted from the end of <i>str</i>. Returns - * <code>nil</code> if the initial offset falls outside the string, the length + * an offset is negative, it is counted from the end of *str*. Returns + * `nil` if the initial offset falls outside the string, the length * is negative, or the beginning of the range is greater than the end. * - * If a <code>String</code> is given, that string is returned if it occurs in - * <i>str</i>. In both cases, <code>nil</code> is returned if there is no + * If a `String` is given, that string is returned if it occurs in + * *str*. In both cases, `nil` is returned if there is no * match. * * a = "hello there" @@ -1417,12 +1622,12 @@ str_escape(mrb_state *mrb, mrb_value str, mrb_bool inspect) } static void -mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_value replace) +mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen, mrb_value replace) { mrb_int beg, len, charlen; mrb_ensure_string_type(mrb, replace); - switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) { + switch (str_convert_range(mrb, str, idx, alen, &beg, &len)) { case STR_OUT_OF_RANGE: default: mrb_raise(mrb, E_INDEX_ERROR, "string not matched"); @@ -1432,7 +1637,7 @@ mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_ } charlen = RSTRING_CHAR_LEN(str); if (beg < 0) { beg += charlen; } - if (beg < 0 || beg > charlen) { str_out_of_index(mrb, indx); } + if (beg < 0 || beg > charlen) { str_out_of_index(mrb, idx); } /* fall through */ case STR_CHAR_RANGE_CORRECTED: beg = chars2bytes(str, 0, beg); @@ -1453,16 +1658,16 @@ mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_ * str[range] = replace * str[other_str] = replace * - * Modify +self+ by replacing the content of +self+. + * Modify `self` by replacing the content of `self`. * The portion of the string affected is determined using the same criteria as +String#[]+. - * The return value of this expression is +replace+. + * The return value of this expression is `replace`. */ static mrb_value mrb_str_aset_m(mrb_state *mrb, mrb_value str) { - mrb_value indx, alen, replace; + mrb_value idx, alen, replace; - switch (mrb_get_args(mrb, "oo|S!", &indx, &alen, &replace)) { + switch (mrb_get_args(mrb, "oo|S!", &idx, &alen, &replace)) { case 2: replace = alen; alen = mrb_undef_value(); @@ -1470,7 +1675,7 @@ mrb_str_aset_m(mrb_state *mrb, mrb_value str) case 3: break; } - mrb_str_aset(mrb, str, indx, alen, replace); + mrb_str_aset(mrb, str, idx, alen, replace); return replace; } @@ -1479,8 +1684,8 @@ mrb_str_aset_m(mrb_state *mrb, mrb_value str) * call-seq: * str.capitalize! => str or nil * - * Modifies <i>str</i> by converting the first character to uppercase and the - * remainder to lowercase. Returns <code>nil</code> if no changes are made. + * Modifies *str* by converting the first character to uppercase and the + * remainder to lowercase. Returns `nil` if no changes are made. * * a = "hello" * a.capitalize! #=> "Hello" @@ -1517,7 +1722,7 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) * call-seq: * str.capitalize => new_str * - * Returns a copy of <i>str</i> with the first character converted to uppercase + * Returns a copy of *str* with the first character converted to uppercase * and the remainder to lowercase. * * "hello".capitalize #=> "Hello" @@ -1527,9 +1732,7 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_capitalize(mrb_state *mrb, mrb_value self) { - mrb_value str; - - str = mrb_str_dup(mrb, self); + mrb_value str = mrb_str_dup(mrb, self); mrb_str_capitalize_bang(mrb, str); return str; } @@ -1539,23 +1742,18 @@ mrb_str_capitalize(mrb_state *mrb, mrb_value self) * call-seq: * str.chomp!(separator="\n") => str or nil * - * Modifies <i>str</i> in place as described for <code>String#chomp</code>, - * returning <i>str</i>, or <code>nil</code> if no modifications were made. + * Modifies *str* in place as described for `String#chomp`, + * returning *str*, or `nil` if no modifications were made. */ static mrb_value mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) { mrb_value rs; - mrb_int newline; - char *p, *pp; - mrb_int rslen; - mrb_int len; - mrb_int argc; + mrb_int argc = mrb_get_args(mrb, "|S", &rs); struct RString *s = mrb_str_ptr(str); - argc = mrb_get_args(mrb, "|S", &rs); mrb_str_modify_keep_ascii(mrb, s); - len = RSTR_LEN(s); + mrb_int len = RSTR_LEN(s); if (argc == 0) { if (len == 0) return mrb_nil_value(); smart_chomp: @@ -1577,8 +1775,8 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) } if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value(); - p = RSTR_PTR(s); - rslen = RSTRING_LEN(rs); + char *p = RSTR_PTR(s); + mrb_int rslen = RSTRING_LEN(rs); if (rslen == 0) { while (len>0 && p[len-1] == '\n') { len--; @@ -1593,13 +1791,13 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) return mrb_nil_value(); } if (rslen > len) return mrb_nil_value(); - newline = RSTRING_PTR(rs)[rslen-1]; + mrb_int newline = RSTRING_PTR(rs)[rslen-1]; if (rslen == 1 && newline == '\n') newline = RSTRING_PTR(rs)[rslen-1]; if (rslen == 1 && newline == '\n') goto smart_chomp; - pp = p + len - rslen; + char *pp = p + len - rslen; if (p[len-1] == newline && (rslen <= 1 || memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) { @@ -1615,10 +1813,10 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) * call-seq: * str.chomp(separator="\n") => new_str * - * Returns a new <code>String</code> with the given record separator removed - * from the end of <i>str</i> (if present). <code>chomp</code> also removes - * carriage return characters (that is it will remove <code>\n</code>, - * <code>\r</code>, and <code>\r\n</code>). + * Returns a new `String` with the given record separator removed + * from the end of *str* (if present). `chomp` also removes + * carriage return characters (that is it will remove `\n`, + * `\r`, and `\r\n`). * * "hello".chomp #=> "hello" * "hello\n".chomp #=> "hello" @@ -1631,9 +1829,7 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_chomp(mrb_state *mrb, mrb_value self) { - mrb_value str; - - str = mrb_str_dup(mrb, self); + mrb_value str = mrb_str_dup(mrb, self); mrb_str_chomp_bang(mrb, str); return str; } @@ -1643,9 +1839,9 @@ mrb_str_chomp(mrb_state *mrb, mrb_value self) * call-seq: * str.chop! => str or nil * - * Processes <i>str</i> as for <code>String#chop</code>, returning <i>str</i>, - * or <code>nil</code> if <i>str</i> is the empty string. See also - * <code>String#chomp!</code>. + * Processes *str* as for `String#chop`, returning *str*, + * or `nil` if *str* is the empty string. See also + * `String#chomp!`. */ static mrb_value mrb_str_chop_bang(mrb_state *mrb, mrb_value str) @@ -1685,10 +1881,10 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str) * call-seq: * str.chop => new_str * - * Returns a new <code>String</code> with the last character removed. If the - * string ends with <code>\r\n</code>, both characters are removed. Applying - * <code>chop</code> to an empty string returns an empty - * string. <code>String#chomp</code> is often a safer alternative, as it leaves + * Returns a new `String` with the last character removed. If the + * string ends with `\r\n`, both characters are removed. Applying + * `chop` to an empty string returns an empty + * string. `String#chomp` is often a safer alternative, as it leaves * the string unchanged if it doesn't end in a record separator. * * "string\r\n".chop #=> "string" @@ -1700,8 +1896,7 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_chop(mrb_state *mrb, mrb_value self) { - mrb_value str; - str = mrb_str_dup(mrb, self); + mrb_value str = mrb_str_dup(mrb, self); mrb_str_chop_bang(mrb, str); return str; } @@ -1711,7 +1906,7 @@ mrb_str_chop(mrb_state *mrb, mrb_value self) * call-seq: * str.downcase! => str or nil * - * Downcases the contents of <i>str</i>, returning <code>nil</code> if no + * Downcases the contents of *str*, returning `nil` if no * changes were made. */ static mrb_value @@ -1741,7 +1936,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) * call-seq: * str.downcase => new_str * - * Returns a copy of <i>str</i> with all uppercase letters replaced with their + * Returns a copy of *str* with all uppercase letters replaced with their * lowercase counterparts. The operation is locale insensitive---only * characters 'A' to 'Z' are affected. * @@ -1750,9 +1945,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_downcase(mrb_state *mrb, mrb_value self) { - mrb_value str; - - str = mrb_str_dup(mrb, self); + mrb_value str = mrb_str_dup(mrb, self); mrb_str_downcase_bang(mrb, str); return str; } @@ -1762,7 +1955,7 @@ mrb_str_downcase(mrb_state *mrb, mrb_value self) * call-seq: * str.empty? => true or false * - * Returns <code>true</code> if <i>str</i> has a length of zero. + * Returns `true` if *str* has a length of zero. * * "hello".empty? #=> false * "".empty? #=> true @@ -1791,6 +1984,18 @@ mrb_str_eql(mrb_state *mrb, mrb_value self) return mrb_bool_value(eql_p); } +/* + * @param mrb The mruby state. + * @param str The mruby string from which to take a substring. + * @param beg The starting character index of the substring. + * @param len The length in characters of the substring. + * @return A new mruby string representing the substring, or nil if out of bounds. + * + * Creates a new mruby string that is a substring of an existing string. + * This function considers character indices (which might differ from byte indices + * if UTF-8 is enabled) and length. + * Handles negative indices and adjusts length to fit within string boundaries. + */ MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { @@ -1860,7 +2065,7 @@ mrb_str_hash_m(mrb_state *mrb, mrb_value self) * str.include? other_str => true or false * str.include? int => true or false * - * Returns <code>true</code> if <i>str</i> contains the given string or + * Returns `true` if *str* contains the given string or * character. * * "hello".include? "lo" #=> true @@ -1882,8 +2087,8 @@ mrb_str_include(mrb_state *mrb, mrb_value self) * call-seq: * str.byteindex(substring, offset = 0) -> integer or nil * - * Returns the \Integer byte-based index of the first occurrence of the given +substring+, - * or +nil+ if none found: + * Returns the \Integer byte-based index of the first occurrence of the given `substring`, + * or `nil` if none found: * * 'foo'.byteindex('f') # => 0 * 'foo'.byteindex('oo') # => 1 @@ -1916,7 +2121,7 @@ mrb_str_byteindex_m(mrb_state *mrb, mrb_value str) * str.index(substring [, offset]) => int or nil * * Returns the index of the first occurrence of the given - * <i>substring</i>. Returns <code>nil</code> if not found. + * *substring*. Returns `nil` if not found. * If the second parameter is present, it * specifies the position in the string to begin the search. * @@ -1978,7 +2183,7 @@ mrb_str_replace(mrb_state *mrb, mrb_value str) * call-seq: * String.new(str="") => new_str * - * Returns a new string object containing a copy of <i>str</i>. + * Returns a new string object containing a copy of *str*. */ static mrb_value mrb_str_init(mrb_state *mrb, mrb_value self) @@ -1999,7 +2204,7 @@ mrb_str_init(mrb_state *mrb, mrb_value self) * str.intern => symbol * str.to_sym => symbol * - * Returns the <code>Symbol</code> corresponding to <i>str</i>, creating the + * Returns the `Symbol` corresponding to *str*, creating the * symbol if it did not previously exist. * * "Koala".intern #=> :Koala @@ -2009,16 +2214,35 @@ mrb_str_init(mrb_state *mrb, mrb_value self) * s == :@cat #=> true * * This can also be used to create symbols that cannot be represented using the - * <code>:xxx</code> notation. + * `:xxx` notation. * * 'cat and dog'.to_sym #=> :"cat and dog" */ +/* + * @param mrb The mruby state. + * @param self The mruby string to convert to a symbol. + * @return The mruby symbol corresponding to the string. + * + * Converts a mruby string to a symbol. If the symbol does not exist, it is created. + */ MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self) { return mrb_symbol_value(mrb_intern_str(mrb, self)); } /* ---------------------------------- */ +/* + * @param mrb The mruby state. + * @param obj The mruby value to convert to a string. + * @return The string representation of the mruby value. + * + * Converts any mruby object to its string representation. + * For strings, it returns the object itself. + * For symbols, it returns the symbol's name as a string. + * For integers, it converts the integer to a string (base 10). + * For classes/modules, it returns their name. + * For other types, it calls the `to_s` method on the object. + */ MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj) { @@ -2038,6 +2262,13 @@ mrb_obj_as_string(mrb_state *mrb, mrb_value obj) } } +/* + * @param mrb The mruby state. + * @param p The pointer to convert. + * @return A new mruby string representing the pointer address. + * + * Converts a C pointer to a mruby string representation (e.g., "0x..."). + */ MRB_API mrb_value mrb_ptr_to_str(mrb_state *mrb, void *p) { @@ -2085,7 +2316,7 @@ str_reverse(char *p, char *e) * call-seq: * str.reverse! => str * - * Reverses <i>str</i> in place. + * Reverses *str* in place. */ static mrb_value mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) @@ -2130,7 +2361,7 @@ mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) * call-seq: * str.reverse => new_str * - * Returns a new string with the characters from <i>str</i> in reverse order. + * Returns a new string with the characters from *str* in reverse order. * * "stressed".reverse #=> "desserts" */ @@ -2146,8 +2377,8 @@ mrb_str_reverse(mrb_state *mrb, mrb_value str) * call-seq: * byterindex(substring, offset = self.bytesize) -> integer or nil * - * Returns the \Integer byte-based index of the _last_ occurrence of the given +substring+, - * or +nil+ if none found: + * Returns the \Integer byte-based index of the _last_ occurrence of the given `substring`, + * or `nil` if none found: * * 'foo'.byterindex('f') # => 0 * 'foo'.byterindex('o') # => 2 @@ -2157,9 +2388,9 @@ mrb_str_reverse(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_byterindex_m(mrb_state *mrb, mrb_value str) { + mrb_int len = RSTRING_LEN(str); mrb_value sub; mrb_int pos; - mrb_int len = RSTRING_LEN(str); if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) { pos = len; @@ -2185,8 +2416,8 @@ mrb_str_byterindex_m(mrb_state *mrb, mrb_value str) * call-seq: * str.rindex(substring [, offset]) => int or nil * - * Returns the index of the last occurrence of the given <i>substring</i>. - * Returns <code>nil</code> if not found. If the second parameter is + * Returns the index of the last occurrence of the given *substring*. + * Returns `nil` if not found. If the second parameter is * present, it specifies the position in the string to end the * search---characters beyond this point will not be considered. * @@ -2239,20 +2470,20 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) * call-seq: * str.split(separator=nil, [limit]) => anArray * - * Divides <i>str</i> into substrings based on a delimiter, returning an array + * Divides *str* into substrings based on a delimiter, returning an array * of these substrings. * - * If <i>separator</i> is a <code>String</code>, then its contents are used as - * the delimiter when splitting <i>str</i>. If <i>separator</i> is a single - * space, <i>str</i> is split on whitespace, with leading whitespace and runs + * If *separator* is a `String`, then its contents are used as + * the delimiter when splitting *str*. If *separator* is a single + * space, *str* is split on whitespace, with leading whitespace and runs * of contiguous whitespace characters ignored. * - * If <i>separator</i> is omitted or <code>nil</code> (which is the default), - * <i>str</i> is split on whitespace as if ' ' were specified. + * If *separator* is omitted or `nil` (which is the default), + * *str* is split on whitespace as if ' ' were specified. * - * If the <i>limit</i> parameter is omitted, trailing null fields are - * suppressed. If <i>limit</i> is a positive number, at most that number of - * fields will be returned (if <i>limit</i> is <code>1</code>, the entire + * If the *limit* parameter is omitted, trailing null fields are + * suppressed. If *limit* is a positive number, at most that number of + * fields will be returned (if *limit* is `1`, the entire * string is returned as the only entry in an array). If negative, there is no * limit to the number of fields returned, and trailing null fields are not * suppressed. @@ -2269,18 +2500,14 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_split_m(mrb_state *mrb, mrb_value str) { - mrb_int argc; mrb_value spat = mrb_nil_value(); enum {awk, string} split_type = string; mrb_int i = 0; - mrb_int beg; - mrb_int end; mrb_int lim = 0; - mrb_bool lim_p; - mrb_value result, tmp; + mrb_value tmp; - argc = mrb_get_args(mrb, "|oi", &spat, &lim); - lim_p = (lim > 0 && argc == 2); + mrb_int argc = mrb_get_args(mrb, "|oi", &spat, &lim); + mrb_bool lim_p = (lim > 0 && argc == 2); if (argc == 2) { if (lim == 1) { if (RSTRING_LEN(str) == 0) @@ -2300,16 +2527,16 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) split_type = awk; } - result = mrb_ary_new(mrb); - beg = 0; + mrb_value result = mrb_ary_new(mrb); + mrb_int beg = 0; if (split_type == awk) { mrb_bool skip = TRUE; - mrb_int idx = 0; mrb_int str_len = RSTRING_LEN(str); - unsigned int c; + mrb_int idx = beg; + mrb_int end = beg; int ai = mrb_gc_arena_save(mrb); + unsigned int c; - idx = end = beg; while (idx < str_len) { c = (unsigned char)RSTRING_PTR(str)[idx++]; if (skip) { @@ -2341,6 +2568,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) int ai = mrb_gc_arena_save(mrb); while (idx < str_len) { + mrb_int end; if (pat_len > 0) { end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx); if (end < 0) break; @@ -2560,6 +2788,18 @@ mrb_str_len_to_integer(mrb_state *mrb, const char *str, size_t len, mrb_int base } /* obsolete: use RSTRING_CSTR() or mrb_string_cstr() */ +/* + * @deprecated Use `RSTRING_CSTR()` or `mrb_string_cstr()` instead. + * @param mrb The mruby state. + * @param ptr Pointer to the mruby string value. + * @return A pointer to a null-terminated C string. + * + * Ensures the mruby string pointed to by `ptr` is a string and returns its + * C string representation. If the string contains null bytes, it raises an + * E_ARGUMENT_ERROR. If the string is not null-terminated, it modifies the + * string in place to add a null terminator (this might involve unsharing + * the string buffer). + */ MRB_API const char* mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) { @@ -2586,21 +2826,42 @@ mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) return RSTR_PTR(ps); } +/* + * @param mrb The mruby state. + * @param str The mruby string value. + * @return A pointer to a null-terminated C string. + * + * Ensures the mruby string `str` is a string and returns its C string representation. + * This is a convenience wrapper around `mrb_string_value_cstr`. + * If the string contains null bytes, it raises an E_ARGUMENT_ERROR. + * If the string is not null-terminated, it modifies the string in place + * to add a null terminator. + */ MRB_API const char* mrb_string_cstr(mrb_state *mrb, mrb_value str) { return mrb_string_value_cstr(mrb, &str); } +/* + * @param mrb The mruby state. + * @param str The mruby string to convert. + * @param base The base for conversion (0 or 2-36). + * @param badcheck If `TRUE`, raise an error on invalid input; otherwise, return 0. + * @return An mruby integer value. + * + * Converts an mruby string to an mruby integer. + * Interprets leading characters in `str` as an integer of the specified `base`. + * If `base` is 0, it auto-detects the base (0x for hex, 0b for binary, 0o or 0 for octal, else decimal). + * If `badcheck` is true, invalid characters will raise an `E_ARGUMENT_ERROR`. + * Otherwise, extraneous characters are ignored, and 0 is returned for invalid numbers. + */ MRB_API mrb_value mrb_str_to_integer(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) { - const char *s; - mrb_int len; - mrb_ensure_string_type(mrb, str); - s = RSTRING_PTR(str); - len = RSTRING_LEN(str); + const char *s = RSTRING_PTR(str); + mrb_int len = RSTRING_LEN(str); return mrb_str_len_to_integer(mrb, s, len, base, badcheck); } @@ -2609,10 +2870,10 @@ mrb_str_to_integer(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badchec * call-seq: * str.to_i(base=10) => integer * - * Returns the result of interpreting leading characters in <i>str</i> as an - * integer base <i>base</i> (between 2 and 36). Extraneous characters past the + * Returns the result of interpreting leading characters in *str* as an + * integer base *base* (between 2 and 36). Extraneous characters past the * end of a valid number are ignored. If there is not a valid number at the - * start of <i>str</i>, <code>0</code> is returned. This method never raises an + * start of *str*, `0` is returned. This method never raises an * exception. * * "12345".to_i #=> 12345 @@ -2638,6 +2899,7 @@ mrb_str_to_i(mrb_state *mrb, mrb_value self) } #ifndef MRB_NO_FLOAT +/* Internal helper for mrb_str_to_dbl */ static double mrb_str_len_to_dbl(mrb_state *mrb, const char *s, size_t len, mrb_bool badcheck) { @@ -2726,6 +2988,17 @@ bad: return d; } +/* + * @param mrb The mruby state. + * @param str The mruby string to convert. + * @param badcheck If `TRUE`, raise an error on invalid input; otherwise, return 0.0. + * @return A C double value. + * + * Converts an mruby string to a C double. + * Interprets leading characters in `str` as a floating-point number. + * If `badcheck` is true, invalid characters will raise an `E_ARGUMENT_ERROR`. + * Otherwise, extraneous characters are ignored, and 0.0 is returned for invalid numbers. + */ MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) { @@ -2737,10 +3010,10 @@ mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) * call-seq: * str.to_f => float * - * Returns the result of interpreting leading characters in <i>str</i> as a + * Returns the result of interpreting leading characters in *str* as a * floating-point number. Extraneous characters past the end of a valid number - * are ignored. If there is not a valid number at the start of <i>str</i>, - * <code>0.0</code> is returned. This method never raises an exception. + * are ignored. If there is not a valid number at the start of *str*, + * `0.0` is returned. This method never raises an exception. * * "123.45e1".to_f #=> 1234.5 * "45.67 degrees".to_f #=> 45.67 @@ -2774,7 +3047,7 @@ mrb_str_to_s(mrb_state *mrb, mrb_value self) * call-seq: * str.upcase! => str or nil * - * Upcases the contents of <i>str</i>, returning <code>nil</code> if no changes + * Upcases the contents of *str*, returning `nil` if no changes * were made. */ static mrb_value @@ -2804,7 +3077,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) * call-seq: * str.upcase => new_str * - * Returns a copy of <i>str</i> with all lowercase letters replaced with their + * Returns a copy of *str* with all lowercase letters replaced with their * uppercase counterparts. The operation is locale insensitive---only * characters 'a' to 'z' are affected. * @@ -2813,9 +3086,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_upcase(mrb_state *mrb, mrb_value self) { - mrb_value str; - - str = mrb_str_dup(mrb, self); + mrb_value str = mrb_str_dup(mrb, self); mrb_str_upcase_bang(mrb, str); return str; } @@ -2824,8 +3095,8 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self) * call-seq: * str.dump -> new_str * - * Produces a version of <i>str</i> with all nonprinting characters replaced by - * <code>\nnn</code> notation and all special characters escaped. + * Produces a version of *str* with all nonprinting characters replaced by + * `\nnn` notation and all special characters escaped. */ mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str) @@ -2833,12 +3104,21 @@ mrb_str_dump(mrb_state *mrb, mrb_value str) return str_escape(mrb, str, FALSE); } +/* + * @param mrb The mruby state. + * @param str The mruby string to append to (modified in place). + * @param ptr A pointer to the C string to append. + * @param len The length of the C string to append. + * @return The modified mruby string `str`. + * + * Appends a C string of a given length to an mruby string. + * The mruby string `str` is modified in place. Handles resizing and + * potential overlap if `ptr` is within `str`'s buffer. + */ MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) { struct RString *s = mrb_str_ptr(str); - mrb_int capa; - mrb_int total; ptrdiff_t off = -1; if (len == 0) return str; @@ -2847,7 +3127,8 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) off = ptr - RSTR_PTR(s); } - capa = RSTR_CAPA(s); + mrb_int capa = RSTR_CAPA(s); + mrb_int total; if (mrb_int_add_overflow(RSTR_LEN(s), len, &total)) { size_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); @@ -2868,12 +3149,30 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) return str; } +/* + * @param mrb The mruby state. + * @param str The mruby string to append to (modified in place). + * @param ptr A pointer to the null-terminated C string to append. + * @return The modified mruby string `str`. + * + * Appends a null-terminated C string to an mruby string. + * The mruby string `str` is modified in place. + */ MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr) { return mrb_str_cat(mrb, str, ptr, ptr ? strlen(ptr) : 0); } +/* + * @param mrb The mruby state. + * @param str The mruby string to append to (modified in place). + * @param str2 The mruby string to append. + * @return The modified mruby string `str`. + * + * Appends an mruby string (`str2`) to another mruby string (`str`). + * The mruby string `str` is modified in place. Handles self-appendage. + */ MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) { @@ -2883,6 +3182,17 @@ mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2)); } +/* + * @param mrb The mruby state. + * @param str1 The mruby string to append to (modified in place). + * @param str2 The mruby value to append (will be converted to a string if not already one). + * @return The modified mruby string `str1`. + * + * Appends an mruby value (`str2`) to an mruby string (`str1`). + * `str2` is first ensured to be a string (converted if necessary). + * Then, `str1` is modified in place. This is similar to `mrb_str_concat` + * but `mrb_str_concat` takes `self` and `other` as parameters. + */ MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { @@ -2901,6 +3211,15 @@ mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) * str[3] = "\b" * str.inspect #=> "\"hel\\bo\"" */ +/* + * @param mrb The mruby state. + * @param str The mruby string to inspect. + * @return A new mruby string that is the inspect-representation of `str`. + * + * Returns a human-readable, printable version of the string, typically + * surrounded by quotes and with special characters escaped. + * UTF-8 characters are preserved if `MRB_UTF8_STRING` is defined and `inspect` is true. + */ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str) { @@ -2934,7 +3253,7 @@ mrb_str_bytes(mrb_state *mrb, mrb_value str) * call-seq: * str.getbyte(index) -> 0 .. 255 * - * returns the <i>index</i>th byte as an integer. + * returns the *index*th byte as an integer. */ static mrb_value mrb_str_getbyte(mrb_state *mrb, mrb_value str) @@ -2954,17 +3273,16 @@ mrb_str_getbyte(mrb_state *mrb, mrb_value str) * call-seq: * str.setbyte(index, integer) -> integer * - * modifies the <i>index</i>th byte as <i>integer</i>. + * modifies the *index*th byte as *integer*. */ static mrb_value mrb_str_setbyte(mrb_state *mrb, mrb_value str) { mrb_int pos, byte; - mrb_int len; mrb_get_args(mrb, "ii", &pos, &byte); - len = RSTRING_LEN(str); + mrb_int len = RSTRING_LEN(str); if (pos < -len || len <= pos) mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of string", pos); if (pos < 0) @@ -2987,8 +3305,8 @@ mrb_str_setbyte(mrb_state *mrb, mrb_value str) * objects, returns a substring starting at the offset given by the first, and * a length given by the second. If given a Range, a substring containing * bytes at offsets given by the range is returned. In all three cases, if - * an offset is negative, it is counted from the end of <i>str</i>. Returns - * <code>nil</code> if the initial offset falls outside the string, the length + * an offset is negative, it is counted from the end of *str*. Returns + * `nil` if the initial offset falls outside the string, the length * is negative, or the beginning of the range is greater than the end. * The encoding of the resulted string keeps original encoding. * @@ -3043,10 +3361,12 @@ sub_replace(mrb_state *mrb, mrb_value self) char *p, *match; mrb_int plen, mlen; mrb_int found, offset; - mrb_value result; mrb_get_args(mrb, "ssi", &p, &plen, &match, &mlen, &found); - result = mrb_str_new(mrb, 0, 0); + if (found < 0 || RSTRING_LEN(self) < found) { + mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range"); + } + mrb_value result = mrb_str_new(mrb, 0, 0); for (mrb_int i=0; i<plen; i++) { if (p[i] != '\\' || i+1==plen) { mrb_str_cat(mrb, result, p+i, 1); @@ -3130,13 +3450,15 @@ str_bytesplice(mrb_state *mrb, mrb_value str, mrb_int idx1, mrb_int len1, mrb_va * bytesplice(range, str) -> string * bytesplice(range, str, str_range) -> string * - * Replaces some or all of the content of +self+ with +str+, and returns +self+. + * Replaces some or all of the content of `self` with `str`, and returns `self`. * The portion of the string affected is determined using - * the same criteria as String#byteslice, except that +length+ cannot be omitted. + * the same criteria as String#byteslice, except that `length` cannot be omitted. * If the replacement string is not the same length as the text it is replacing, * the string will be adjusted accordingly. * - * If +str_index+ and +str_length+, or +str_range+ are given, the content of +self+ is replaced by str.byteslice(str_index, str_length) or str.byteslice(str_range); however the substring of +str+ is not allocated as a new string. + * If `str_index` and `str_length`, or `str_range` are given, the content of `self` + * is replaced by str.byteslice(str_index, str_length) or str.byteslice(str_range); + * however the substring of `str` is not allocated as a new string. * * The form that take an Integer will raise an IndexError if the value is out * of range; the Range form will raise a RangeError. @@ -3185,6 +3507,58 @@ mrb_encoding(mrb_state *mrb, mrb_value self) } /* ---------------------------*/ +static const mrb_mt_entry string_rom_entries[] = { + MRB_MT_ENTRY(mrb_str_bytesize, MRB_SYM(bytesize), MRB_ARGS_NONE()), + MRB_MT_ENTRY(mrb_str_cmp_m, MRB_OPSYM(cmp), MRB_ARGS_REQ(1)), /* 15.2.10.5.1 */ + MRB_MT_ENTRY(mrb_str_equal_m, MRB_OPSYM(eq), MRB_ARGS_REQ(1)), /* 15.2.10.5.2 */ + MRB_MT_ENTRY(mrb_str_plus_m, MRB_OPSYM(add), MRB_ARGS_REQ(1)), /* 15.2.10.5.4 */ + MRB_MT_ENTRY(mrb_str_times, MRB_OPSYM(mul), MRB_ARGS_REQ(1)), /* 15.2.10.5.5 */ + MRB_MT_ENTRY(mrb_str_aref_m, MRB_OPSYM(aref), MRB_ARGS_ANY()), /* 15.2.10.5.6 */ + MRB_MT_ENTRY(mrb_str_aset_m, MRB_OPSYM(aset), MRB_ARGS_ANY()), + MRB_MT_ENTRY(mrb_str_capitalize, MRB_SYM(capitalize), MRB_ARGS_NONE()), /* 15.2.10.5.7 */ + MRB_MT_ENTRY(mrb_str_capitalize_bang, MRB_SYM_B(capitalize), MRB_ARGS_NONE()), /* 15.2.10.5.8 */ + MRB_MT_ENTRY(mrb_str_chomp, MRB_SYM(chomp), MRB_ARGS_ANY()), /* 15.2.10.5.9 */ + MRB_MT_ENTRY(mrb_str_chomp_bang, MRB_SYM_B(chomp), MRB_ARGS_ANY()), /* 15.2.10.5.10 */ + MRB_MT_ENTRY(mrb_str_chop, MRB_SYM(chop), MRB_ARGS_NONE()), /* 15.2.10.5.11 */ + MRB_MT_ENTRY(mrb_str_chop_bang, MRB_SYM_B(chop), MRB_ARGS_NONE()), /* 15.2.10.5.12 */ + MRB_MT_ENTRY(mrb_str_downcase, MRB_SYM(downcase), MRB_ARGS_NONE()), /* 15.2.10.5.13 */ + MRB_MT_ENTRY(mrb_str_downcase_bang, MRB_SYM_B(downcase), MRB_ARGS_NONE()), /* 15.2.10.5.14 */ + MRB_MT_ENTRY(mrb_str_empty_p, MRB_SYM_Q(empty), MRB_ARGS_NONE()), /* 15.2.10.5.16 */ + MRB_MT_ENTRY(mrb_str_eql, MRB_SYM_Q(eql), MRB_ARGS_REQ(1)), /* 15.2.10.5.17 */ + MRB_MT_ENTRY(mrb_str_hash_m, MRB_SYM(hash), MRB_ARGS_NONE()), /* 15.2.10.5.20 */ + MRB_MT_ENTRY(mrb_str_include, MRB_SYM_Q(include), MRB_ARGS_REQ(1)), /* 15.2.10.5.21 */ + MRB_MT_ENTRY(mrb_str_index_m, MRB_SYM(index), MRB_ARGS_ARG(1,1)), /* 15.2.10.5.22 */ + MRB_MT_ENTRY(mrb_str_init, MRB_SYM(initialize), MRB_ARGS_OPT(1) | MRB_MT_PRIVATE), /* 15.2.10.5.23 */ + MRB_MT_ENTRY(mrb_str_replace, MRB_SYM(initialize_copy), MRB_ARGS_REQ(1) | MRB_MT_PRIVATE), /* 15.2.10.5.24 */ + MRB_MT_ENTRY(mrb_str_intern, MRB_SYM(intern), MRB_ARGS_NONE()), /* 15.2.10.5.25 */ + MRB_MT_ENTRY(mrb_str_size, MRB_SYM(length), MRB_ARGS_NONE()), /* 15.2.10.5.26 */ + MRB_MT_ENTRY(mrb_str_replace, MRB_SYM(replace), MRB_ARGS_REQ(1)), /* 15.2.10.5.28 */ + MRB_MT_ENTRY(mrb_str_reverse, MRB_SYM(reverse), MRB_ARGS_NONE()), /* 15.2.10.5.29 */ + MRB_MT_ENTRY(mrb_str_reverse_bang, MRB_SYM_B(reverse), MRB_ARGS_NONE()), /* 15.2.10.5.30 */ + MRB_MT_ENTRY(mrb_str_rindex_m, MRB_SYM(rindex), MRB_ARGS_ANY()), /* 15.2.10.5.31 */ + MRB_MT_ENTRY(mrb_str_size, MRB_SYM(size), MRB_ARGS_NONE()), /* 15.2.10.5.33 */ + MRB_MT_ENTRY(mrb_str_aref_m, MRB_SYM(slice), MRB_ARGS_ANY()), /* 15.2.10.5.34 */ + MRB_MT_ENTRY(mrb_str_split_m, MRB_SYM(split), MRB_ARGS_ANY()), /* 15.2.10.5.35 */ + MRB_MT_ENTRY(mrb_str_to_i, MRB_SYM(to_i), MRB_ARGS_ANY()), /* 15.2.10.5.39 */ + MRB_MT_ENTRY(mrb_str_to_s, MRB_SYM(to_s), MRB_ARGS_NONE()), /* 15.2.10.5.40 */ + MRB_MT_ENTRY(mrb_str_to_s, MRB_SYM(to_str), MRB_ARGS_NONE()), + MRB_MT_ENTRY(mrb_str_intern, MRB_SYM(to_sym), MRB_ARGS_NONE()), /* 15.2.10.5.41 */ + MRB_MT_ENTRY(mrb_str_upcase, MRB_SYM(upcase), MRB_ARGS_NONE()), /* 15.2.10.5.42 */ + MRB_MT_ENTRY(mrb_str_upcase_bang, MRB_SYM_B(upcase), MRB_ARGS_NONE()), /* 15.2.10.5.43 */ + MRB_MT_ENTRY(mrb_str_inspect, MRB_SYM(inspect), MRB_ARGS_NONE()), /* 15.2.10.5.46(x) */ + MRB_MT_ENTRY(mrb_str_bytes, MRB_SYM(bytes), MRB_ARGS_NONE()), + MRB_MT_ENTRY(mrb_str_getbyte, MRB_SYM(getbyte), MRB_ARGS_REQ(1)), + MRB_MT_ENTRY(mrb_str_setbyte, MRB_SYM(setbyte), MRB_ARGS_REQ(2)), + MRB_MT_ENTRY(mrb_str_byteindex_m, MRB_SYM(byteindex), MRB_ARGS_ARG(1,1)), + MRB_MT_ENTRY(mrb_str_byterindex_m, MRB_SYM(byterindex), MRB_ARGS_ARG(1,1)), + MRB_MT_ENTRY(mrb_str_byteslice, MRB_SYM(byteslice), MRB_ARGS_ARG(1,1)), + MRB_MT_ENTRY(mrb_str_bytesplice, MRB_SYM(bytesplice), MRB_ARGS_ANY()), + MRB_MT_ENTRY(sub_replace, MRB_SYM(__sub_replace), MRB_ARGS_REQ(3)), /* internal */ +#ifndef MRB_NO_FLOAT + MRB_MT_ENTRY(mrb_str_to_f, MRB_SYM(to_f), MRB_ARGS_NONE()), /* 15.2.10.5.38 */ +#endif +}; + void mrb_init_string(mrb_state *mrb) { @@ -3196,60 +3570,7 @@ mrb_init_string(mrb_state *mrb) mrb->string_class = s = mrb_define_class_id(mrb, MRB_SYM(String), mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); - mrb_define_method_id(mrb, s, MRB_SYM(bytesize), mrb_str_bytesize, MRB_ARGS_NONE()); - - mrb_define_method_id(mrb, s, MRB_OPSYM(cmp), mrb_str_cmp_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.1 */ - mrb_define_method_id(mrb, s, MRB_OPSYM(eq), mrb_str_equal_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.2 */ - mrb_define_method_id(mrb, s, MRB_OPSYM(add), mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */ - mrb_define_method_id(mrb, s, MRB_OPSYM(mul), mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */ - mrb_define_method_id(mrb, s, MRB_OPSYM(aref), mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */ - mrb_define_method_id(mrb, s, MRB_OPSYM(aset), mrb_str_aset_m, MRB_ARGS_ANY()); - mrb_define_method_id(mrb, s, MRB_SYM(capitalize), mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */ - mrb_define_method_id(mrb, s, MRB_SYM_B(capitalize), mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */ - mrb_define_method_id(mrb, s, MRB_SYM(chomp), mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */ - mrb_define_method_id(mrb, s, MRB_SYM_B(chomp), mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */ - mrb_define_method_id(mrb, s, MRB_SYM(chop), mrb_str_chop, MRB_ARGS_NONE()); /* 15.2.10.5.11 */ - mrb_define_method_id(mrb, s, MRB_SYM_B(chop), mrb_str_chop_bang, MRB_ARGS_NONE()); /* 15.2.10.5.12 */ - mrb_define_method_id(mrb, s, MRB_SYM(downcase), mrb_str_downcase, MRB_ARGS_NONE()); /* 15.2.10.5.13 */ - mrb_define_method_id(mrb, s, MRB_SYM_B(downcase), mrb_str_downcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.14 */ - mrb_define_method_id(mrb, s, MRB_SYM_Q(empty), mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */ - mrb_define_method_id(mrb, s, MRB_SYM_Q(eql), mrb_str_eql, MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */ - - mrb_define_method_id(mrb, s, MRB_SYM(hash), mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */ - mrb_define_method_id(mrb, s, MRB_SYM_Q(include), mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */ - mrb_define_method_id(mrb, s, MRB_SYM(index), mrb_str_index_m, MRB_ARGS_ARG(1,1)); /* 15.2.10.5.22 */ - mrb_define_method_id(mrb, s, MRB_SYM(initialize), mrb_str_init, MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */ - mrb_define_method_id(mrb, s, MRB_SYM(initialize_copy), mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */ - mrb_define_method_id(mrb, s, MRB_SYM(intern), mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.25 */ - mrb_define_method_id(mrb, s, MRB_SYM(length), mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.26 */ - mrb_define_method_id(mrb, s, MRB_SYM(replace), mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */ - mrb_define_method_id(mrb, s, MRB_SYM(reverse), mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */ - mrb_define_method_id(mrb, s, MRB_SYM_B(reverse), mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */ - mrb_define_method_id(mrb, s, MRB_SYM(rindex), mrb_str_rindex_m, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ - mrb_define_method_id(mrb, s, MRB_SYM(size), mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */ - mrb_define_method_id(mrb, s, MRB_SYM(slice), mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */ - mrb_define_method_id(mrb, s, MRB_SYM(split), mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */ - -#ifndef MRB_NO_FLOAT - mrb_define_method_id(mrb, s, MRB_SYM(to_f), mrb_str_to_f, MRB_ARGS_NONE()); /* 15.2.10.5.38 */ -#endif - mrb_define_method_id(mrb, s, MRB_SYM(to_i), mrb_str_to_i, MRB_ARGS_ANY()); /* 15.2.10.5.39 */ - mrb_define_method_id(mrb, s, MRB_SYM(to_s), mrb_str_to_s, MRB_ARGS_NONE()); /* 15.2.10.5.40 */ - mrb_define_method_id(mrb, s, MRB_SYM(to_str), mrb_str_to_s, MRB_ARGS_NONE()); - mrb_define_method_id(mrb, s, MRB_SYM(to_sym), mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.41 */ - mrb_define_method_id(mrb, s, MRB_SYM(upcase), mrb_str_upcase, MRB_ARGS_NONE()); /* 15.2.10.5.42 */ - mrb_define_method_id(mrb, s, MRB_SYM_B(upcase), mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */ - mrb_define_method_id(mrb, s, MRB_SYM(inspect), mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */ - mrb_define_method_id(mrb, s, MRB_SYM(bytes), mrb_str_bytes, MRB_ARGS_NONE()); - - mrb_define_method_id(mrb, s, MRB_SYM(getbyte), mrb_str_getbyte, MRB_ARGS_REQ(1)); - mrb_define_method_id(mrb, s, MRB_SYM(setbyte), mrb_str_setbyte, MRB_ARGS_REQ(2)); - mrb_define_method_id(mrb, s, MRB_SYM(byteindex), mrb_str_byteindex_m, MRB_ARGS_ARG(1,1)); - mrb_define_method_id(mrb, s, MRB_SYM(byterindex), mrb_str_byterindex_m, MRB_ARGS_ARG(1,1)); - mrb_define_method_id(mrb, s, MRB_SYM(byteslice), mrb_str_byteslice, MRB_ARGS_ARG(1,1)); - mrb_define_method_id(mrb, s, MRB_SYM(bytesplice), mrb_str_bytesplice, MRB_ARGS_ANY()); - - mrb_define_method_id(mrb, s, MRB_SYM(__sub_replace), sub_replace, MRB_ARGS_REQ(3)); /* internal */ + MRB_MT_INIT_ROM(mrb, s, string_rom_entries); mrb_define_method_id(mrb, mrb->kernel_module, MRB_SYM(__ENCODING__), mrb_encoding, MRB_ARGS_NONE()); } |
