6 ms·
He's nearly there, but the reason for the number 23 is staring you right in the face. It's not arbitrary. A heap allocated string requires 8 bytes for the len
by minimax 15y ago
He's nearly there, but the reason for the number 23 is staring you right in the face. It's not arbitrary. A heap allocated string requires 8 bytes for the length, 8 bytes for the pointer to the string, and 8 bytes for the capacity / reference count. The sum is 24 bytes.
So you either use it as a length/pointer/capacity/refcount struct, or save the malloc and use the 24 bytes directly for 23 chars and a NULL terminator.
- nonrecursive 15y agoI think Pat actually points that out: The value of RSTRING_EMBED_LEN_MAX was chosen to match the size of the len/ptr/capa values. That’s where the 23 limit comes from.
- steve19 15y agoI apologize, I accidentally downvoted you instead of upvoting.
- charliesome 15y agouse the 24 bytes directly for 23 chars and a NULL terminator. Ruby strings aren't null terminated though EDIT: So I went and looked at the code, they are null terminated, but as far as I can tell, Ruby doesn't rely on this directly.
- deleted 15y ago[deleted]
- throw_away 15y agoThey probably are when they are inlined like this, else, how would you know the length?
- charliesome 15y agoThe length is stored in a flag. include/ruby/ruby.h: #define RSTRING_EMBED_LEN(str) \ (long)((RBASIC(str)->flags >> RSTRING_EMBED_LEN_SHIFT) & \ (RSTRING_EMBED_LEN_MASK >> RSTRING_EMBED_LEN_SHIFT)) #define RSTRING_LEN(str) \ (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ RSTRING_EMBED_LEN(str) : \ RSTRING(str)->as.heap.len)
- iclelland 15y agoPardon? The code is right there in the article -- in this case, for this specific kind of string, at the C level, they very clearly are null-terminated strings. No, you don't get to see that at the Ruby level, it's all nicely abstracted away, but that is exactly what is happening inside the VM.
- charliesome 15y agoWell I dunno how they could possibly be null terminated if you can stick a null in any old string: >> "hello\0world".length => 11
- zokier 15y agoThat could be explained with ruby doing escaping of null characters automatically in the background, although I don't believe that to be the case.
- ComputerGuru 15y agoNot really. RString operates in the same way std::string does - it has a character array and it has a member variable denoting the length. It's not null-terminated. You can store a sequence of nulls and that will not affect the result of std::string.size() In C, you'll be forgiven for thinking it was null terminated, because attempting to assign a std::string a value from a null-containing array of characters would terminate the copy upon reaching the null, but that's only because the original char array is null-terminated when read as a C string. However, you can manually construct a std::string with \0 sequences in the middle and that will not terminate the string, nor affect the separate length calculation. The same applies for Ruby's RString. So that was the reason they're not null-terminated. Now the reason why they technically are (i.e. the reason NULLs are stored at the end of the string) is for compatibility and optimization. At the cost of one byte per string (for the trailing \0), we get instant compatibility with non-RStrng/std::string functions. If a function needs a C string, we can just pass the internal pointer to the character array - no need to copy the string to a temporary buffer and append a null. Therefore, while null-termination is absolutely NOT required when dealing with an exclusively counted-length implementation of C strings (a la RString, CString, std::string, etc.) if you can just pass the pointer and the length separately, it would be a ridiculously foolish optimization for a general string implementation to NOT have the option of directly exposing the underlying null-terminated string to any functions that need it, with the caveat that null-containing counted strings will obviously terminate sooner than expected.
- dchest 15y agoIt seems like the length is stored inside RString's RBasic->flags: #define RSTRING_EMBED_LEN(str) \ (long)((RBASIC(str)->flags >> RSTRING_EMBED_LEN_SHIFT) & \ (RSTRING_EMBED_LEN_MASK >> RSTRING_EMBED_LEN_SHIFT))
- deleted 15y ago[deleted]
- dkarl 15y agobut as far as I can tell, Ruby doesn't rely on this directly They may be null-terminated to make writing C extensions easier. If they weren't, you'd have to make a null-terminated copy every time you wanted to pass the bytes to a function expecting a null-terminated string, which most C APIs do.