mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-23 08:55:52 +08:00
Introduce wwrite_to_fd
wwrite_to_fd() is a function which writes a wide string to a file descriptor without performing any heap allocations.
This commit is contained in:
parent
a0cb23bea5
commit
2809d637db
|
@ -5025,6 +5025,49 @@ static void test_wcstring_tok() {
|
|||
}
|
||||
}
|
||||
|
||||
static void test_wwrite_to_fd() {
|
||||
say(L"Testing wwrite_to_fd");
|
||||
char t[] = "/tmp/fish_test_wwrite.XXXXXX";
|
||||
if (!mktemp(t)) {
|
||||
err(L"Unable to create temporary file");
|
||||
return;
|
||||
}
|
||||
size_t sizes[] = {0, 1, 2, 3, 5, 13, 23, 64, 128, 255, 4096, 4096 * 2};
|
||||
for (size_t size : sizes) {
|
||||
autoclose_fd_t fd{open(t, O_RDWR | O_TRUNC | O_CREAT, 0666)};
|
||||
if (!fd.valid()) {
|
||||
wperror(L"open");
|
||||
err(L"Unable to open temporary file");
|
||||
return;
|
||||
}
|
||||
wcstring input{};
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
input.push_back(wchar_t(random()));
|
||||
}
|
||||
|
||||
ssize_t amt = wwrite_to_fd(input, fd.fd());
|
||||
if (amt < 0) {
|
||||
wperror(L"write");
|
||||
err(L"Unable to write to temporary file");
|
||||
return;
|
||||
}
|
||||
std::string narrow = wcs2string(input);
|
||||
size_t expected_size = narrow.size();
|
||||
do_test(amt == expected_size);
|
||||
|
||||
if (lseek(fd.fd(), 0, SEEK_SET) < 0) {
|
||||
wperror(L"seek");
|
||||
err(L"Unable to seek temporary file");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string contents(expected_size, '\0');
|
||||
ssize_t read_amt = read(fd.fd(), &contents[0], expected_size);
|
||||
do_test(read_amt >= 0 && static_cast<size_t>(read_amt) == expected_size);
|
||||
}
|
||||
(void)remove(t);
|
||||
}
|
||||
|
||||
static void test_pcre2_escape() {
|
||||
say(L"Testing escaping strings as pcre2 literals");
|
||||
// plain text should not be needlessly escaped
|
||||
|
@ -5928,6 +5971,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
if (should_test_function("utility_functions")) test_utility_functions();
|
||||
if (should_test_function("wcstring_tok")) test_wcstring_tok();
|
||||
if (should_test_function("wwrite_to_fd")) test_wwrite_to_fd();
|
||||
if (should_test_function("env_vars")) test_env_vars();
|
||||
if (should_test_function("env")) test_env_snapshot();
|
||||
if (should_test_function("str_to_num")) test_str_to_num();
|
||||
|
|
|
@ -536,6 +536,56 @@ int wrename(const wcstring &old, const wcstring &newv) {
|
|||
return rename(old_narrow.c_str(), new_narrow.c_str());
|
||||
}
|
||||
|
||||
ssize_t wwrite_to_fd(const wchar_t *input, size_t input_len, int fd) {
|
||||
// Accumulate data in a local buffer.
|
||||
char accum[512];
|
||||
size_t accumlen{0};
|
||||
constexpr size_t maxaccum = sizeof accum / sizeof *accum;
|
||||
|
||||
// Helper to perform a write to 'fd', looping as necessary.
|
||||
// \return true on success, false on error.
|
||||
ssize_t total_written = 0;
|
||||
auto do_write = [fd, &total_written](const char *cursor, size_t remaining) {
|
||||
while (remaining > 0) {
|
||||
ssize_t samt = write(fd, cursor, remaining);
|
||||
if (samt < 0) return false;
|
||||
total_written += samt;
|
||||
size_t amt = static_cast<size_t>(samt);
|
||||
assert(amt <= remaining && "Wrote more than requested");
|
||||
remaining -= amt;
|
||||
cursor += amt;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Helper to flush the accumulation buffer.
|
||||
auto flush_accum = [&] {
|
||||
if (!do_write(accum, accumlen)) return false;
|
||||
accumlen = 0;
|
||||
return true;
|
||||
};
|
||||
|
||||
bool success = wcs2string_callback(input, input_len, [&](const char *buff, size_t len) {
|
||||
if (len + accumlen > maxaccum) {
|
||||
// We have to flush.
|
||||
// Note this modifies 'accumlen'.
|
||||
if (!flush_accum()) return false;
|
||||
}
|
||||
if (len + accumlen <= maxaccum) {
|
||||
// Accumulate more.
|
||||
memmove(accum + accumlen, buff, len);
|
||||
accumlen += len;
|
||||
return true;
|
||||
} else {
|
||||
// Too much data to even fit, just write it immediately.
|
||||
return do_write(buff, len);
|
||||
}
|
||||
});
|
||||
// Flush any remaining.
|
||||
if (success) success = flush_accum();
|
||||
return success ? total_written : -1;
|
||||
}
|
||||
|
||||
/// Return one if the code point is in a Unicode private use area.
|
||||
int fish_is_pua(wint_t wc) {
|
||||
if (PUA1_START <= wc && wc < PUA1_END) return 1;
|
||||
|
|
11
src/wutil.h
11
src/wutil.h
|
@ -113,6 +113,17 @@ int wmkdir(const wcstring &name, int mode);
|
|||
/// Wide character version of rename.
|
||||
int wrename(const wcstring &oldName, const wcstring &newv);
|
||||
|
||||
/// Write a wide string to a file descriptor. This avoids doing any additional allocation.
|
||||
/// This does NOT retry on EINTR or EAGAIN, it simply returns.
|
||||
/// \return -1 on error in which case errno will have been set. In this event, the number of bytes
|
||||
/// actually written cannot be obtained.
|
||||
ssize_t wwrite_to_fd(const wchar_t *s, size_t len, int fd);
|
||||
|
||||
/// Variant of above that accepts a wcstring.
|
||||
inline ssize_t wwrite_to_fd(const wcstring &s, int fd) {
|
||||
return wwrite_to_fd(s.c_str(), s.size(), fd);
|
||||
}
|
||||
|
||||
#define PUA1_START 0xE000
|
||||
#define PUA1_END 0xF900
|
||||
#define PUA2_START 0xF0000
|
||||
|
|
Loading…
Reference in New Issue
Block a user