News for hackers. Stuff that matters.
Re:Designing a better strcpy by Hackbraten on Saturday June 19, 2021 @04:41PM (#27564283)
Not everyone is going to be happy about 8 bytes overhead for every buffer.

Also, not every C-style API will migrate. If you consume one of those, you may still have to count the length, which costs cycles.

Re:Designing a better strcpy by scottlamb on Saturday June 19, 2021 @06:03PM (#27564902)
> Not everyone is going to be happy about 8 bytes overhead for every buffer.

Then prefix it with four bytes (which, if you omit the trailing NUL, adds three bytes in total). If your C strings are longer than 2^32 bytes, you're probably doing something wrong.

I also should point out that memory is typically cheap. Cache is more precious, and I'd expect on balance adding a length would pollute the cache less than scanning through the whole string unnecessarily.

But I agree with your second point:

> Also, not every C-style API will migrate. If you consume one of those, you may still have to count the length, which costs cycles.

Similarly, the article started with this text:

> Like them or not, null-terminated strings are essential to C, and working with them is necessary in all but the most trivial programs.

You can do things nicely in your own code, but you still need NUL-terminated strings when dealing with existing code. And dealing with existing code is often the reason to pick C...

Re:Designing a better strcpy by iib on Saturday June 19, 2021 @07:52PM (#27565670)
I would assume the people bothered by an increase in byte-sized overhead are not the ones that are using 2^32 length strings.

About the cache, I think for small strings, scanning the whole string may still be faster.

Though adding a four-byte length prefix would make certain functions like strlen almost go away.

Re:Designing a better strcpy by attractivechaos on Saturday June 19, 2021 @08:22PM (#27565848)
Four bytes are still overhead. Also, you often need to keep the capacity of a string. Another four bytes.

The C string APIs are like the lowest denominator of all use cases. When you know string lengths, you can avoid quadratic strcat() or other performance pitfalls with the existing APIs. On the other hand, if the APIs forced everyone to keep string lengths, performance/memory/convenience would be affected for certain use cases. Memory is not cheap when you deal with many short strings or when you do embedded programming.

Re:Designing a better strcpy by scottlamb on Saturday June 19, 2021 @09:11PM (#27566099)
> Four bytes are still overhead.

I hedged with "typically" but I'm a little skeptical about the environments in which this is a problem. Embedded stuff that's severely memory constrained probably doesn't deal with that many strings.

But you could use a variable-sized length prefix as another commenter suggested to have no overhead on short strings. Its size would be decided at allocation time so you're not shifting the string. You could indicate it via high bits of the length or low bits of 4-byte-aligned string pointers. It's an extra branch or table lookup or the like on access though.

> Also, you often need to keep the capacity of a string. Another four bytes

Nonsense. There are exactly no situations where you need to keep a capacity with this scheme and not with traditional C strings.

Re:Designing a better strcpy by attractivechaos on Sunday June 20, 2021 @09:21AM (#27569671)
For two standard formats in our field, you sometimes need to keep billions of short strings in memory. Many of these strings are 2 bytes or several bytes in length. The parser in C packs them as NULL terminated strings. The C++ and Java parsers use the std::string or the string type. They use a lot more memory. Even a 4-byte overhead would double the memory. Yes, you can have a smart way to keep short strings to waste less or even no memory (one of the two formats is using this trick), but it makes code harder to read and hurts performance due to the extra check/conversion.

As to the capacity field, we sometimes have to keep it around. The question is: do you keep it inside the string struct/memory block (like std::string) or let users handle it? We have varying preferences in different cases. It is hard to design APIs optimal in all cases.

Re:Designing a better strcpy by scottlamb on Sunday June 20, 2021 @12:01PM (#27571339)
Do you mind sharing the field / formats? I'm curious.

I think situations like this are special enough that they shouldn't drive discussion for general-purpose types. fwiw, some ideas for what you're describing based on what understanding I can have from your two-paragraph description:

* If many of billions of text strings are that short, there must be a huge number of duplicates. If they have similar lifetimes (e.g., all freed with the parsed file handle) and are immutable or mutations are rare and can follow a "get_mut()", I'd consider aliasing them with an intern table. (You could do that for all strings or for just the strings under a certain length.) There's some CPU and memory overhead from the hash table but it'd make duplicates use no RAM in a way that's orthogonal to the representation of a particular string.

* Or you could have an 8-byte type that can represent a pointer or a 7-byte text string. With a general-purpose allocator, the low bits of a pointer are guaranteed to be zero, and many of the high bits are also effectively unused (all the same) depending on platform.

(Either of the above might be what you're referring to with "a smart way to keep short strings to waste less or even no memory".)

* On most platforms, malloc returns pointers with a minimum alignment of two words or so and thus a similar minimum allocation size. If you're on a 64-bit platform (must be, with billions of 2+ byte strings in RAM), that's 16 bytes. A lot of waste. A custom allocator might be better. It doesn't have to be anything complex. Again depending on lifetime and mutability, you could use a simple arena (bump allocator). Its handle could be indexes that are valid across reallocation or they could be stable pointers by just making a single upper bound allocation and depending on the OS's lazy minor page faulting to keep the unused part from being backed by real physical memory.

* C++: if you pass these to interfaces that take an absl::string_view or the like rather than const std::string&, you have a lot more flexibility in your representation without copying/reallocating on use.

* Java: GCed languages double memory requirements to begin with for the GC to work efficiently, and Java historically uses a UTF-16 string type (maybe there's an optimization in latest versions?), so it's almost hopeless. Hard for me to reconcile "we need to save RAM" with Java at all. The most RAM-efficient way to store them would be outside Java's heap (native code or mmaped region) but then you have to deal with non-idiomatic handles and likely copying/allocating (generating garbage) on use, yuck.

> As to the capacity field, we sometimes have to keep it around. The question is: do you keep it inside the string struct/memory block (like std::string) or let users handle it? We have varying preferences in different cases. It is hard to design APIs optimal in all cases.

Keep it around long-term, as in you have lots of mutable strings at once rather than having them for a short time then "freezing" them into a more efficient immutable type that can assume length=capacity, can use interning and arenas, etc? Then you want to make sure you're not paying 8 bytes for that capacity field, as you can easily end up with having the caller keep it due to padding. (That'd be a more error-prone API too.) You could have your handles be a single word that uses unused pointer bits for size of capacity and length fields and points to a capacity+length+data allocation. That's likely what I would do if efficiency really matters.

Re:Designing a better strcpy by mkcg on Saturday June 19, 2021 @11:26PM (#27566842)
You could also prefix it with a varint : in this case it would not require any extra byte compared to the NUL terminated string when the length of the string is less than 128 chars.
Re:Designing a better strcpy by jjav on Wednesday June 23, 2021 @08:48PM (#27612528)
> I also should point out that memory is typically cheap.

Not in the early 70s, no.

Even in the late 80s I was writing code very conscious of every single byte that could be saved anywhere.

Re:Designing a better strcpy by rectang on Saturday June 19, 2021 @09:11PM (#27566101)
Saving 8 bytes, and saving a few cycles by scanning rather than counting... is an argument for using NUL-terminated strings as an optimization in specialized circumstances.

In the common case, the extra 8 bytes and the cycles saved (if any) don't matter — and are not worth the sacrifice of making it harder to program correctly.

Re:Designing a better strcpy by kevin_thibedeau on Sunday June 20, 2021 @01:09AM (#27567274)
> the extra 8 bytes and the cycles saved (if any) don't matter

You wouldn't last in embedded development with that sort of attitude. When you're nearly out of RAM you can't afford to waste overhead of fat strings.

Re:Designing a better strcpy by pjmlp on Sunday June 20, 2021 @03:39AM (#27567982)
I was using Turbo Basic, Turbo Pascal, Turbo C++ (with C++ collections), and Clipper on MS-DOS, with 640 KB of memory.

It was just fine, then there are those that nowadays even toy with C++17 on C64.

“Rich Code for Tiny Computers: A Simple Commodore 64 Game in C++17”

https://www.youtube.com/watch?v=zBkNBP00wJE

Really, unless we are talking about PIC and AVRs with like 4KB, we are optimizing for the wrong target.

Re:Designing a better strcpy by mh7 on Sunday June 20, 2021 @05:46AM (#27568550)
You can't afford to waste cycles on O(n) strlen either.
Re:Designing a better strcpy by pkaye on Sunday June 20, 2021 @03:05AM (#27567829)
Not every embedded system is memory constrained these days.
Re:Designing a better strcpy by rectang on Sunday June 20, 2021 @04:02AM (#27568079)
Indeed. And besides, didn't I account for precisely that scenario? Memory-constrained embedded development is a specialized situation that could call for using NUL-terminated strings.
Re:Designing a better strcpy by loup-vaillant on Saturday June 19, 2021 @06:10PM (#27564956)
Actually, it's 7 bytes (you can remove `\0` at the end), and that's if you insist on being able to exceed 4GB with your strings.

Personally, depending on the use case, I'd be willing to have strings limited to 256 bytes (no overhead), 64K bytes (1 byte of overhead), and 4G bytes (3 bytes of overhead). Though combining them all might be quite the nightmare…

Re:Designing a better strcpy by codesnik on Saturday June 19, 2021 @06:23PM (#27565044)
you can also have something akin to MIDI encoding with higher bit reserved to mark that next byte is a part of length field, too. up to 128 bytes with no overhead, up to 16kb with one byte overhead, and so on. Such a detail would be easy to hide in the library. But I wouldn't be surprised that alignment and other troubles would eat any possible gain.
Re:Designing a better strcpy by habibur on Sunday June 20, 2021 @03:14AM (#27567870)
> But I wouldn't be surprised that alignment ... troubles would eat any possible gain.

It's string. Alignment shouldn't be a problem. Rather alignment issues are for the pointers to that string.

Re:Designing a better strcpy by WalterBright on Saturday June 19, 2021 @06:45PM (#27565203)
Won't work with UTF-8.
Re:Designing a better strcpy by thrwaeasddsaf on Saturday June 19, 2021 @07:49PM (#27565650)
Works with any encoding and arbitrary binary data. The last byte of the length field doesn't have the high bit set, and whatever follows after that is completely irrelevant.

But still needless complexity for this.

Re:Designing a better strcpy by moonchild on Saturday June 19, 2021 @10:31PM (#27566587)
Sure it is, the length is out of band. In fact, you could even use utf-8 to encode the length!
Re:Designing a better strcpy by dnr on Saturday June 19, 2021 @09:35PM (#27566242)
Don't forget supporting small strings (<= 7 bytes) in the pointer itself using tag bits. No allocation or dereferencing required.
Re:Designing a better strcpy by habibur on Sunday June 20, 2021 @03:06AM (#27567838)
Microsoft's MFC used to do this in their first version. But later ditched it. Maybe they found it not worth the trouble of checking for this condition everywhere. Increase in code.
Re:Designing a better strcpy by gpderetta on Sunday June 20, 2021 @05:19AM (#27568429)
Thse days, in C++, this is routinely done in various std::string implementations (which are both null terminated and have a separate length). In fact the standard was updated to explicitly allow this.
Re:Designing a better strcpy by WalterBright on Saturday June 19, 2021 @04:57PM (#27564409)
The proposal does not take away any C features.