static void test_ensure(void) { struct sol_buffer buf = SOL_BUFFER_INIT_EMPTY; const int size = 1024; char *buf_data; int i; sol_buffer_ensure(&buf, size); memset(buf.data, 22, size); sol_buffer_ensure(&buf, size * 2); buf_data = buf.data; for (i = 0; i < size; i++) { ASSERT_INT_EQ(buf_data[i], 22); } sol_buffer_ensure(&buf, size / 2); buf_data = buf.data; for (i = 0; i < size / 2; i++) { ASSERT_INT_EQ(buf_data[i], 22); } sol_buffer_fini(&buf); }
SOL_API int sol_buffer_append_slice(struct sol_buffer *buf, const struct sol_str_slice slice) { const size_t nul_size = nul_byte_size(buf); char *p; size_t new_size; int err; SOL_NULL_CHECK(buf, -EINVAL); err = sol_util_size_add(buf->used, slice.len, &new_size); if (err < 0) return err; /* Extra room for the ending NUL-byte. */ if (new_size >= SIZE_MAX - nul_size) return -EOVERFLOW; err = sol_buffer_ensure(buf, new_size + nul_size); if (err < 0) return err; p = sol_buffer_at_end(buf); memcpy(p, slice.data, slice.len); if (nul_size) p[slice.len] = '\0'; buf->used += slice.len; return 0; }
SOL_API int sol_buffer_set_char_at(struct sol_buffer *buf, size_t pos, char c) { int err; const size_t nul_size = nul_byte_size(buf); SOL_NULL_CHECK(buf, -EINVAL); if (buf->used < pos) { return -EINVAL; } /* Extra room for the ending NUL-byte. */ if (1 >= SIZE_MAX - nul_size - pos) return -EOVERFLOW; err = sol_buffer_ensure(buf, pos + 1 + nul_size); if (err < 0) return err; *((char *)buf->data + pos) = c; if (pos + 1 >= buf->used) buf->used = pos + 1; if (nul_byte_size(buf)) return sol_buffer_ensure_nul_byte(buf); return 0; }
SOL_API int sol_buffer_append_char(struct sol_buffer *buf, const char c) { char *p; size_t new_size; int err; SOL_NULL_CHECK(buf, -EINVAL); err = sol_util_size_add(buf->used, 1, &new_size); if (err < 0) return err; err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at_end(buf); *p = c; buf->used++; if (nul_byte_size(buf)) return sol_buffer_ensure_nul_byte(buf); return 0; }
static void test_no_nul_byte(void) { struct sol_buffer buf; int32_t backend; int32_t value = 0xdeadbeef; int err; sol_buffer_init_flags(&buf, &backend, sizeof(backend), SOL_BUFFER_FLAGS_MEMORY_NOT_OWNED | SOL_BUFFER_FLAGS_NO_NUL_BYTE); err = sol_buffer_ensure(&buf, sizeof(int32_t)); ASSERT_INT_EQ(err, 0); err = sol_buffer_append_slice(&buf, SOL_STR_SLICE_STR((const char *)&value, sizeof(value))); ASSERT_INT_EQ(err, 0); err = sol_buffer_append_slice(&buf, SOL_STR_SLICE_STR((const char *)&value, sizeof(value))); ASSERT_INT_NE(err, 0); sol_buffer_fini(&buf); sol_buffer_init_flags(&buf, NULL, 0, SOL_BUFFER_FLAGS_NO_NUL_BYTE); err = sol_buffer_append_printf(&buf, "123"); ASSERT_INT_EQ(err, 0); err = sol_buffer_append_printf(&buf, "4"); ASSERT_INT_EQ(err, 0); ASSERT(sol_str_slice_eq(sol_buffer_get_slice(&buf), SOL_STR_SLICE_STR("1234", 4))); sol_buffer_fini(&buf); }
SOL_API int sol_buffer_insert_char(struct sol_buffer *buf, size_t pos, const char c) { char *p; size_t new_size; int err; SOL_NULL_CHECK(buf, -EINVAL); SOL_INT_CHECK(pos, > buf->used, -EINVAL); if (pos == buf->used) return sol_buffer_append_char(buf, c); err = sol_util_size_add(buf->used, 1, &new_size); if (err < 0) return err; err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at(buf, pos); memmove(p + 1, p, buf->used - pos); *p = c; buf->used++; if (nul_byte_size(buf)) return sol_buffer_ensure_nul_byte(buf); return 0; }
SOL_API int sol_buffer_set_slice_at(struct sol_buffer *buf, size_t pos, const struct sol_str_slice slice) { int err; const size_t nul_size = nul_byte_size(buf); SOL_NULL_CHECK(buf, -EINVAL); if (buf->used < pos) { return -EINVAL; } /* Extra room for the ending NUL-byte. */ if (slice.len >= SIZE_MAX - nul_size - pos) return -EOVERFLOW; err = sol_buffer_ensure(buf, pos + slice.len + nul_size); if (err < 0) return err; /* deal with possible overlaps with memmove */ memmove((char *)buf->data + pos, slice.data, slice.len); if (pos + slice.len >= buf->used) { buf->used = pos + slice.len; /* only terminate if we're growing */ ((char *)buf->data)[buf->used] = 0; } return 0; }
/* Extra room for the ending NUL-byte, necessary even when the buffer has * SOL_BUFFER_FLAGS_NO_NUL_BYTE because vsnprintf always write the '\0'. */ SOL_API int sol_buffer_append_vprintf(struct sol_buffer *buf, const char *fmt, va_list args) { va_list args_copy; size_t space; char *p; ssize_t done; int r; SOL_NULL_CHECK(buf, -EINVAL); SOL_NULL_CHECK(fmt, -EINVAL); /* We need to copy args since we first try to do one vsnprintf() * to the existing space and if it doesn't fit we do a second * one. The second one would fail since args may be changed by * vsnprintf(), so the copy. */ va_copy(args_copy, args); space = buf->capacity - buf->used; p = sol_buffer_at_end(buf); done = vsnprintf(p, space, fmt, args_copy); if (done < 0) { r = -errno; goto end; } else if ((size_t)done >= space) { size_t new_size; r = sol_util_size_add(buf->used, done, &new_size); if (r < 0) goto end; /* Extra room for the ending NUL-byte. */ if (new_size >= SIZE_MAX - 1) { r = -EOVERFLOW; goto end; } r = sol_buffer_ensure(buf, new_size + 1); if (r < 0) goto end; space = buf->capacity - buf->used; p = sol_buffer_at_end(buf); done = vsnprintf(p, space, fmt, args); if (done < 0) { r = -errno; goto end; } } buf->used += done; r = 0; end: va_end(args_copy); return r; }
SOL_API ssize_t sol_util_fill_buffer(int fd, struct sol_buffer *buffer, size_t size) { size_t bytes_read = 0, s; unsigned int retry = 0; ssize_t ret; SOL_NULL_CHECK(buffer, -EINVAL); if (sol_util_size_add(buffer->used, size, &s) < 0) return -EOVERFLOW; ret = sol_buffer_ensure(buffer, s); if (ret < 0) return ret; do { ret = read(fd, (char *)buffer->data + buffer->used + bytes_read, size - bytes_read); if (ret < 0) { retry++; if (retry >= SOL_UTIL_MAX_READ_ATTEMPTS) { if (errno == EINTR || errno == EAGAIN) { ret = 0; /* if we exceed maximum attempts, don't return error */ } break; } if (errno == EINTR || errno == EAGAIN) continue; else break; } else if (ret == 0) { retry++; if (retry >= SOL_UTIL_MAX_READ_ATTEMPTS) break; continue; } retry = 0; //We only count consecutive failures bytes_read += (size_t)ret; } while (bytes_read < size); buffer->used += bytes_read; if (ret >= 0) ret = bytes_read; if (SOL_BUFFER_NEEDS_NUL_BYTE(buffer)) { int err; err = sol_buffer_ensure_nul_byte(buffer); SOL_INT_CHECK(err, < 0, err); } return ret; }
SOL_API int sol_buffer_insert_from_base16(struct sol_buffer *buf, size_t pos, const struct sol_str_slice slice, enum sol_decode_case decode_case) { char *p; size_t new_size; ssize_t decoded_size, r; const size_t nul_size = nul_byte_size(buf); int err; SOL_NULL_CHECK(buf, -EINVAL); SOL_INT_CHECK(pos, > buf->used, -EINVAL); if (slice.len == 0) return 0; if (pos == buf->used) return sol_buffer_append_from_base16(buf, slice, decode_case); decoded_size = sol_util_base16_calculate_decoded_len(slice); if (decoded_size < 0) return decoded_size; err = sol_util_size_add(buf->used, decoded_size, &new_size); if (err < 0) return err; if (nul_size) { err = sol_util_size_add(new_size, nul_size, &new_size); if (err < 0) return err; } err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at(buf, pos); memmove(p + decoded_size, p, buf->used - pos); r = sol_util_base16_decode(p, decoded_size, slice, decode_case); if (r != decoded_size) { memmove(p, p + decoded_size, buf->used - pos); if (nul_size) sol_buffer_ensure_nul_byte(buf); if (r < 0) return r; else return -EINVAL; } buf->used += decoded_size; if (nul_size) return sol_buffer_ensure_nul_byte(buf); return 0; }
SOL_API int sol_buffer_insert_as_base64(struct sol_buffer *buf, size_t pos, const struct sol_str_slice slice, const char base64_map[SOL_STATIC_ARRAY_SIZE(65)]) { char *p; size_t new_size; ssize_t encoded_size, r; const size_t nul_size = nul_byte_size(buf); int err; SOL_NULL_CHECK(buf, -EINVAL); SOL_INT_CHECK(pos, > buf->used, -EINVAL); if (slice.len == 0) return 0; if (pos == buf->used) return sol_buffer_append_as_base64(buf, slice, base64_map); encoded_size = sol_util_base64_calculate_encoded_len(slice, base64_map); if (encoded_size < 0) return encoded_size; err = sol_util_size_add(buf->used, encoded_size, &new_size); if (err < 0) return err; if (nul_size) { err = sol_util_size_add(new_size, nul_size, &new_size); if (err < 0) return err; } err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at(buf, pos); memmove(p + encoded_size, p, buf->used - pos); r = sol_util_base64_encode(p, encoded_size, slice, base64_map); if (r != encoded_size) { memmove(p, p + encoded_size, buf->used - pos); if (nul_size) sol_buffer_ensure_nul_byte(buf); if (r < 0) return r; else return -EINVAL; } buf->used += encoded_size; if (nul_size) return sol_buffer_ensure_nul_byte(buf); return 0; }
static void test_memory_not_owned(void) { struct sol_buffer buf; struct sol_str_slice slice; char backend[10]; int err; sol_buffer_init_flags(&buf, backend, sizeof(backend), SOL_BUFFER_FLAGS_MEMORY_NOT_OWNED); err = sol_buffer_ensure(&buf, 0); ASSERT_INT_EQ(err, 0); /* ensure considers null-byte, so we use sizeof - 1 */ err = sol_buffer_ensure(&buf, sizeof(backend) - 1); ASSERT_INT_EQ(err, 0); err = sol_buffer_ensure(&buf, sizeof(backend) * 2); ASSERT_INT_EQ(err, -ENOMEM); err = sol_buffer_resize(&buf, 0); ASSERT_INT_EQ(err, -EPERM); slice = sol_str_slice_from_str("test"); err = sol_buffer_append_slice(&buf, slice); ASSERT_INT_EQ(err, 0); ASSERT_STR_EQ(buf.data, "test"); slice = sol_str_slice_from_str("other"); err = sol_buffer_append_slice(&buf, slice); ASSERT_INT_EQ(err, 0); ASSERT_STR_EQ(buf.data, "testother"); slice = sol_str_slice_from_str("OVERFLOW"); err = sol_buffer_append_slice(&buf, slice); ASSERT_INT_EQ(err, -ENOMEM); sol_buffer_fini(&buf); }
static void test_ensure(void) { struct sol_buffer buf = SOL_BUFFER_EMPTY; const int size = 1024; int i; sol_buffer_ensure(&buf, size); memset(buf.data, 22, size); sol_buffer_ensure(&buf, size * 2); for (i = 0; i < size; i++) { ASSERT(buf.data[i] == 22); } sol_buffer_ensure(&buf, size / 2); for (i = 0; i < size / 2; i++) { ASSERT(buf.data[i] == 22); } sol_buffer_fini(&buf); }
SOL_API int sol_buffer_append_from_base64(struct sol_buffer *buf, const struct sol_str_slice slice, const char base64_map[SOL_STATIC_ARRAY_SIZE(65)]) { char *p; size_t new_size; ssize_t decoded_size, r; const size_t nul_size = nul_byte_size(buf); int err; SOL_NULL_CHECK(buf, -EINVAL); if (slice.len == 0) return 0; decoded_size = sol_util_base64_calculate_decoded_len(slice, base64_map); if (decoded_size < 0) return decoded_size; err = sol_util_size_add(buf->used, decoded_size, &new_size); if (err < 0) return err; if (nul_size) { err = sol_util_size_add(new_size, nul_size, &new_size); if (err < 0) return err; } err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at_end(buf); r = sol_util_base64_decode(p, decoded_size, slice, base64_map); if (r != decoded_size) { if (nul_size) sol_buffer_ensure_nul_byte(buf); if (r < 0) return r; else return -EINVAL; } buf->used += decoded_size; if (nul_size) return sol_buffer_ensure_nul_byte(buf); return 0; }
ssize_t sol_util_fill_buffer(const int fd, struct sol_buffer *buffer, const size_t size) { size_t bytes_read = 0, s; unsigned int retry = 0; ssize_t ret; SOL_NULL_CHECK(buffer, -EINVAL); if (sol_util_size_add(buffer->used, size, &s) < 0) return -EOVERFLOW; ret = sol_buffer_ensure(buffer, s); if (ret < 0) return ret; do { ret = read(fd, (char *)buffer->data + buffer->used + bytes_read, size - bytes_read); if (ret < 0) { retry++; if (retry >= SOL_UTIL_MAX_READ_ATTEMPTS) break; if (errno == EINTR || errno == EAGAIN) { continue; } else break; } retry = 0; //We only count consecutive failures bytes_read += (size_t)ret; } while (ret && bytes_read < size); buffer->used += bytes_read; if (ret > 0) ret = bytes_read; if (!(buffer->flags & SOL_BUFFER_FLAGS_NO_NUL_BYTE)) { if (buffer->used == buffer->capacity) SOL_WRN("sol_buffer %p asks for terminating NUL byte, but doesn't have space for it", buffer); else *((char *)buffer->data + buffer->used) = '\0'; } return ret; }
SOL_API int sol_buffer_append_as_base16(struct sol_buffer *buf, const struct sol_str_slice slice, bool uppercase) { char *p; size_t new_size; ssize_t encoded_size, r; const size_t nul_size = nul_byte_size(buf); int err; SOL_NULL_CHECK(buf, -EINVAL); if (slice.len == 0) return 0; encoded_size = sol_util_base16_calculate_encoded_len(slice); if (encoded_size < 0) return encoded_size; err = sol_util_size_add(buf->used, encoded_size, &new_size); if (err < 0) return err; if (nul_size) { err = sol_util_size_add(new_size, nul_size, &new_size); if (err < 0) return err; } err = sol_buffer_ensure(buf, new_size); if (err < 0) return err; p = sol_buffer_at_end(buf); r = sol_util_base16_encode(p, encoded_size, slice, uppercase); if (r != encoded_size) { if (nul_size) sol_buffer_ensure_nul_byte(buf); if (r < 0) return r; else return -EINVAL; } buf->used += encoded_size; if (nul_size) return sol_buffer_ensure_nul_byte(buf); return 0; }
SOL_API int sol_buffer_set_slice(struct sol_buffer *buf, const struct sol_str_slice slice) { int err; SOL_NULL_CHECK(buf, -EINVAL); /* Extra room for the ending NUL-byte. */ if (slice.len >= SIZE_MAX - nul_byte_size(buf)) return -EOVERFLOW; err = sol_buffer_ensure(buf, slice.len + nul_byte_size(buf)); if (err < 0) return err; sol_str_slice_copy(buf->data, slice); buf->used = slice.len; return 0; }
SOL_API int sol_buffer_ensure_nul_byte(struct sol_buffer *buf) { SOL_NULL_CHECK(buf, -EINVAL); if (buf->flags & SOL_BUFFER_FLAGS_NO_NUL_BYTE) return -EINVAL; if (buf->used && *((char *)sol_buffer_at_end(buf) - 1) == '\0') return 0; if (buf->used >= SIZE_MAX - 1 || sol_buffer_ensure(buf, buf->used + 1) < 0) return -ENOMEM; *((char *)buf->data + buf->used) = '\0'; return 0; }
SOL_API int sol_buffer_expand(struct sol_buffer *buf, size_t bytes) { size_t new_size, available; int err; SOL_NULL_CHECK(buf, -EINVAL); available = buf->capacity - buf->used; if (available >= bytes) return 0; err = sol_util_size_add(buf->capacity, bytes - available, &new_size); SOL_INT_CHECK(err, < 0, err); err = sol_buffer_ensure(buf, new_size); SOL_INT_CHECK(err, < 0, err); return 0; }
SOL_API int sol_buffer_insert_slice(struct sol_buffer *buf, size_t pos, const struct sol_str_slice slice) { const size_t nul_size = nul_byte_size(buf); char *p; size_t new_size; int err; SOL_NULL_CHECK(buf, -EINVAL); SOL_INT_CHECK(pos, > buf->used, -EINVAL); if (pos == buf->used) return sol_buffer_append_slice(buf, slice); err = sol_util_size_add(buf->used, slice.len, &new_size); if (err < 0) return err; /* Extra room for the ending NUL-byte. */ if (new_size >= SIZE_MAX - nul_size) return -EOVERFLOW; err = sol_buffer_ensure(buf, new_size + nul_size); if (err < 0) return err; p = sol_buffer_at(buf, pos); memmove(p + slice.len, p, buf->used - pos); memcpy(p, slice.data, slice.len); buf->used += slice.len; if (nul_size) { p = sol_buffer_at_end(buf); p[0] = '\0'; } return 0; }