4 ms·
An even better representation is the one from `folly::FBString`. There, the tag that indicates small string mode is not a particular bound on capacity, but the
by xoranth 3y ago
An even better representation is the one from `folly::FBString`.
There, the tag that indicates small string mode is not a particular bound on capacity, but the last byte of the struct that is set to zero. That way, the tag also acts as a null terminator for the stored string.
- orlp 3y agoI fail to see how that is better. Null-terminated strings are a terrible idea in the first place.
- UncleEntity 3y agoIf null-terminated strings are already a thing in your language and you can just pass an inlined string to whatever function without any bit manipulation I fail to see the downside.
- xoranth 3y ago> I fail to see how that is better. Null-terminated strings are a terrible idea in the first place. TLDR: you get extra bytes in your small strings Long version: Copying from FBString code: struct MediumLarge { Char* data_; size_t size_; size_t capacity_; }; // sizeof(MediumLarge) == 24 struct FBString { union { // For accessing the last byte. uint8_t bytes_[sizeof(MediumLarge)]; Char small_[sizeof(MediumLarge) / sizeof(Char)]; MediumLarge ml_; }; }; Then `bytes_[23]` is set to `24 - size of small string` for a small string. You also "steal" a couple of bits from `capacity_` as a tag to see if a string is small or large. This has two advantages: - You get to reuse seven of the eight bytes of `capacity_` in your small string (i.e. your max small string size is 23, not 16 as it would be with a simpler scheme). - You get a null terminator "for free" (though, of course, you still have size and capacity). This is on top of size/capacity. This could be reduced to 16 bytes by making `size_` and `capacity_` uint32_t instead of size_t.