While some string APIs that use a single block of memory for each string don't have a specific function to read X bytes from this file/socket onto the end of the string, they do have both a request space and a truncate space function. This makes it possible to do zero copy, possibly non-blocking, read IO onto the end of your string.
The idea is that, assuming you have a string s1, a file descriptor fd, and a number of bytes to read num. You can create a function which does...
len = API_len(s1); API_request(s1, num); bytes = read(fd, API_buf(s1) + len, num); if (bytes == -1) { /* deal with the error */ ; bytes = 0; } API_truncate(s1, len + bytes);...and it can be called for non-blocking IO, and be fairly efficient. However note that the string is still going to grow inefficently (possibly copying data a lot) as you request space ... and any space allocated in the string for IO is lost until the string is free'd. This may or may not be a problem, depending.