string.c: ask a string what encoding it is, not whether a bit is set - #7157
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe change replaces legacy binary-string flag macros with explicit encoding identifiers and centralized encoding APIs. Core string operations and related gems now assign or copy encoding metadata through the new interfaces. ChangesString encoding migration
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to This localized change preserves existing string flag behavior and layout, with the reported builds and tests passing; no actionable merge-blocking risk remains. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
`MRB_STR_BINARY` is the whole of the answer to how a string's bytes are
read: a string carrying it is read by byte, a string without it is read
the way the build reads strings by default. The macros around it are
named after the bit rather than after the question, so every write names
one value of the field instead of the field itself:
RSTR_SET_BINARY_FLAG(s);
RSTR_UNSET_BINARY_FLAG(s);
RSTR_COPY_BINARY_FLAG(dst, src);
`include/mruby/string.h` now names the field and the values it takes:
#define MRB_STR_ENCODING_DEFAULT 0
#define MRB_STR_ENCODING_BINARY 1
#ifdef MRB_UTF8_STRING
# define MRB_STR_ENCODING_UTF8 MRB_STR_ENCODING_DEFAULT
#endif
#define RSTR_ENCODING(s)
#define RSTR_ENCODING_SET(s, e)
#define RSTR_ENC_COPY(dst, src)
and the 22 writes and copies go through them. `RSTR_BINARY_P()` stays,
since whether a string is read by byte is a question about the encoding
and not about the shape it is kept in. It reads the field now and
answers 1 rather than 16, which is the narrowing `re_binary_string_p()`
was doing on the way out.
Index 0 is the build's default because a fresh string is zero filled:
the strings the parser hands over, and the ones numbers and times spell
themselves with, then say what they are without a store on the path
every string is made on. `__ENCODING__` names index 0 for a given build,
so it is UTF-8 where `MRB_UTF8_STRING` is set and ASCII-8BIT where it is
not.
Binary cannot share index 0 with the default in a build that indexes by
byte anyway. `String#b` and `Integer#chr` mark strings there too, and
`mruby-regexp` reads the mark through `re_binary_string_p()` to decide
whether to walk the subject a byte at a time, so folding the two
together would change what that build answers.
`MRB_STR_ENCODING_UTF8` is defined only where the build carries UTF-8. A
build without it has no UTF-8 string to name, so naming one should stop
the compiler rather than quietly write the default. The only place that
names it is `String#force_encoding`, in the gem that sets the define.
Every write is bit for bit the write it replaces, so no answer changes
and no test comes with this. Building full-core with `mruby-encoding`
and without it, every object file is identical instruction for
instruction to the one master builds, and `.text` over the whole build
is unchanged in both.
bd82ad0 to
4848467
Compare
MRB_STR_BINARYis the whole of what a string says about how its bytes are read: a string carrying it is read by byte, a string without it is read the way the build reads strings by default. The macros around it are named after the bit rather than after the question, so every write names one value of the field instead of the field:This PR gives the field a name and a small vocabulary, and routes the 22 writes and copies through it. The bit stays where it is;
MRB_STR_BINARYis still 16 and the layout offlagsdoes not move.What goes into
include/mruby/string.hRSTR_SET_BINARY_FLAG(),RSTR_UNSET_BINARY_FLAG()andRSTR_COPY_BINARY_FLAG()go.Index 0 is the build's default
A fresh string is zero filled, so index 0 costs no store on the path every string is made on. That is where the literals the parser hands over end up, and the strings numbers and times spell themselves with.
__ENCODING__is what names index 0 for a given build: UTF-8 whereMRB_UTF8_STRINGis set, ASCII-8BIT where it is not.Binary cannot share index 0 with the default even in a build that indexes by byte anyway.
String#bandInteger#chrmark strings there too, andmruby-regexpreads the mark throughre_binary_string_p()(mrbgems/mruby-regexp/src/regexp.c) to decide whether to walk the subject a byte at a time, so folding the two together would change what that build answers.MRB_STR_ENCODING_UTF8is defined only where the build carries UTF-8. A build without it has no UTF-8 string to name, so naming one should stop the compiler rather than quietly write the default. The only place that names it isString#force_encoding, inmruby-encoding, which sets the define itself.RSTR_BINARY_P()staysWhether a string is read by byte is a question about the encoding, not about the shape the answer is kept in, so the predicate keeps its name and its 18 readers are untouched. It reads the field now and answers 1 rather than 16.
re_binary_string_p()returns it asmrb_bool, which isboolin the usual configurations anduint8_tin the fallback (include/mruby/value.h); 16 and 1 are both true in either, so the narrowing simply happens where the answer is made.The 22 sites
RSTR_SET_BINARY_FLAGsrc/string.c(4),mruby-string-ext(5),mruby-sprintf,mruby-regexp,mruby-string-bitops,mruby-encodingRSTR_UNSET_BINARY_FLAGmruby-encoding(String#force_encoding)RSTR_COPY_BINARY_FLAGsrc/string.c(3),mruby-string-ext(4),mruby-sprintfEach replacement is bit for bit the write it replaces.
RSTR_ENCODING_SET(s, MRB_STR_ENCODING_BINARY)expands toflags = (flags & ~16) | 16, and withMRB_STR_ENCODING_UTF8it expands toflags = (flags & ~16) | 0, since(0 == 1)is always false.Generated code
Two full-core builds, with
mruby-encodingand with the gem removed from the box, gcc-O3:MRB_UTF8_STRING.text1789338 unchangedmruby-encoding.text1771123 unchangedComparison is
objdump -dover every.oin the build against the same object built from master. The files differ as bytes only in debug info, since the line numbers ininclude/mruby/string.hmoved.Testing
rake -m teston both builds above, plus the C++ ABI build, all green:MRB_UTF8_STRING: 2291 tests, 2281 OK, 10 skip, 0 KO, 0 crash, 0 warnings.mruby-encoding, where strings index by byte: 2229 tests, 2200 OK, 29 skip, 0 KO, 0 crash, 0 warnings.enable_cxx_abi: 2291 tests, 2281 OK, 10 skip.No test accompanies the change. No string answers anything different, so there is nothing new to pin.
Rebased on 8bd5bd0
re_byte_substr()inmruby-regexpused to copy the flag by hand and now hands the work tomrb_str_byte_subseq(), so the copy this PR would have converted there is gone. That is the one site the count above lost.Not in this PR
The bit layout of
flagsdoes not change, the 18RSTR_BINARY_P()readers do not change, and the comments that still say "flag" about this bit stay as they are while it still is one.Summary by CodeRabbit