That’s not cache-friendly, though. I think the short string optimization (keeping short strings alongside the string length, but allocating a separate buffer for longer strings. See https://devblogs.microsoft.com/oldnewthing/20240510-00/?p=10... for how various C++ compilers implement that) may be the best option.
How so? The string implementations in that post are pretty much that:
struct string
{
char* ptr;
size_t size;
union {
size_t capacity;
char buf[16];
};
The pointer and the size are stored together, and they may optionally be located right next to the string's actual data, but only for very small, locally-allocated, short-lived strings; but in normal usage, that pointer points somewhere into the heap.Only for small strings. Locally allocated and short-lived aren’t required for short string optimization to take an effect.
Also, I can’t find a good reference, but “only for small strings” in many programs means “for most strings”.
This is a special-case optimisation that I'm happy to lose in favour of the massive performance and security benefits otherwise.
Isn't length + pointer... Basically a Pascal string? Unless I am mistaken.
I think what was unsaid in your second point is that we really need to type-differentiate constant strings, dynamic strings, and string 'views', which Rust does in-language, and C++ does with the standard library. I prefer Rust's approach.
You cannot cheaply take an arbitrary view of the interior string - you can only truncate cheaply (and oob checks are easier to automate). That’s why pointer + length is important because it’s a generic view. For arrays it’s more complicated because you can have a stride which is important for multidimensional arrays.
Length + pointer is a record string, a pascal string has the length at the head of the buffer, behind the pointer.
The main difference is that if a string's length is by its data, you can't easily construct a pointer to part of that data without copying it into a new string, whereas if instead the length is by the data's address, you can cheaply construct pointers to any substring (by coming up with new length+address pairs) without having to construct entire new strings.
And yes, today it would be easier to make a copy of the data... but remember we are talking about 90's, where RAM is measured in megabytes and your L1 cache may be only 8KB or so.