In general it's hard to get more efficient than a simple struct String { const char *buffer; u32 size; }. Your method removes an indirection from the allocated storage, but you'd still need an external pointer to point to that struct in most cases. That, plus retrieving the size now costs an additional dereference. So I wouldn't use your method unless I knew that I'd have to reference the string from multiple locations.
The best way to be efficient is often to make assumptions about the data. Most strings don't need any dynamic allocation after having been "built". So it makes a ton of sense to make a string builder API that returns a final string when it's finished. In this way, you save at least the "allocated" member.
The advantage of the simpler string representation is that it works for any string (or substring) that is contiguous in memory, and is completely decoupled from allocation concerns. E.g. I can easily
, to be able to statically declare such strings like this:
String my_string = STRING("Foo bar");
If you have many strings that you know are small, then just the normal nul-terminated C string (without any size field) is as storage-efficient as it gets.
In practice, I find string handling so easy that I rarely even define this struct String. I just pass around strings to functions as two arguments - pointer + size. It feels so light and data flows so easily between APIs, I love it.
The best way to be efficient is often to make assumptions about the data. Most strings don't need any dynamic allocation after having been "built". So it makes a ton of sense to make a string builder API that returns a final string when it's finished. In this way, you save at least the "allocated" member.
The advantage of the simpler string representation is that it works for any string (or substring) that is contiguous in memory, and is completely decoupled from allocation concerns. E.g. I can easily
, to be able to statically declare such strings like this: If you have many strings that you know are small, then just the normal nul-terminated C string (without any size field) is as storage-efficient as it gets.In practice, I find string handling so easy that I rarely even define this struct String. I just pass around strings to functions as two arguments - pointer + size. It feels so light and data flows so easily between APIs, I love it.