blob: 2aa6994cc9bce941a2434994820feb5fea9fb783 [file] [log] [blame]
// Copyright 2021 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "iree/base/string_builder.h"
#include "iree/base/alignment.h"
// Minimum alignment for storage buffer allocations.
#define IREE_STRING_BUILDER_ALIGNMENT 128
IREE_API_EXPORT void iree_string_builder_initialize(
iree_allocator_t allocator, iree_string_builder_t* out_builder) {
memset(out_builder, 0, sizeof(*out_builder));
out_builder->allocator = allocator;
}
IREE_API_EXPORT void iree_string_builder_initialize_with_storage(
char* buffer, iree_host_size_t buffer_capacity,
iree_string_builder_t* out_builder) {
iree_string_builder_initialize(iree_allocator_null(), out_builder);
out_builder->buffer = buffer;
out_builder->capacity = buffer_capacity;
}
IREE_API_EXPORT void iree_string_builder_deinitialize(
iree_string_builder_t* builder) {
if (builder->buffer != NULL) {
iree_allocator_free(builder->allocator, builder->buffer);
}
memset(builder, 0, sizeof(*builder));
}
static bool iree_string_builder_is_calculating_size(
const iree_string_builder_t* builder) {
return iree_allocator_is_null(builder->allocator) && builder->buffer == NULL;
}
IREE_API_EXPORT const char* iree_string_builder_buffer(
const iree_string_builder_t* builder) {
return builder->buffer;
}
IREE_API_EXPORT iree_host_size_t
iree_string_builder_size(const iree_string_builder_t* builder) {
return builder->size;
}
IREE_API_EXPORT iree_host_size_t
iree_string_builder_capacity(const iree_string_builder_t* builder) {
return builder->capacity;
}
IREE_API_EXPORT iree_string_view_t
iree_string_builder_view(const iree_string_builder_t* builder) {
return iree_make_string_view(iree_string_builder_buffer(builder),
iree_string_builder_size(builder));
}
IREE_API_EXPORT char* iree_string_builder_take_storage(
iree_string_builder_t* builder) {
char* buffer = builder->buffer;
if (builder->size == 0) {
// In empty cases we return NULL and need to clean up inline as the user is
// expecting to be able to discard the builder after this returns.
if (builder->buffer != NULL) {
iree_allocator_free(builder->allocator, builder->buffer);
builder->buffer = NULL;
}
buffer = NULL;
}
builder->size = 0;
builder->capacity = 0;
builder->buffer = NULL;
return buffer;
}
IREE_API_EXPORT iree_status_t iree_string_builder_reserve(
iree_string_builder_t* builder, iree_host_size_t minimum_capacity) {
iree_host_size_t new_capacity = builder->capacity;
if (builder->capacity < minimum_capacity) {
new_capacity =
iree_host_align(minimum_capacity, IREE_STRING_BUILDER_ALIGNMENT);
}
if (builder->capacity >= new_capacity) {
// Already at/above the requested minimum capacity.
return iree_ok_status();
} else if (iree_allocator_is_null(builder->allocator)) {
// No allocator provided and the builder cannot grow.
return iree_make_status(
IREE_STATUS_RESOURCE_EXHAUSTED,
"non-growable builder capacity exceeded (capacity=%" PRIhsz
"; requested=%" PRIhsz ", adjusted=%" PRIhsz ")",
builder->capacity, minimum_capacity, new_capacity);
}
IREE_RETURN_IF_ERROR(iree_allocator_realloc(builder->allocator, new_capacity,
(void**)&builder->buffer));
builder->buffer[builder->size] = 0;
builder->capacity = new_capacity;
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t iree_string_builder_append_inline(
iree_string_builder_t* builder, iree_host_size_t count, char** out_head) {
*out_head = NULL;
if (!iree_string_builder_is_calculating_size(builder)) {
IREE_RETURN_IF_ERROR(iree_string_builder_reserve(
builder, builder->size + count + /*NUL=*/1));
*out_head = &builder->buffer[builder->size];
}
builder->size += count;
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t iree_string_builder_append_string(
iree_string_builder_t* builder, iree_string_view_t value) {
// Ensure capacity for the value + NUL terminator.
if (!iree_string_builder_is_calculating_size(builder)) {
IREE_RETURN_IF_ERROR(
iree_string_builder_reserve(builder, builder->size + value.size + 1));
// Only copy the bytes if we are not doing a size calculation.
memcpy(builder->buffer + builder->size, value.data, value.size);
builder->buffer[builder->size + value.size] = 0; // NUL
}
builder->size += value.size;
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t iree_string_builder_append_cstring(
iree_string_builder_t* builder, const char* value) {
return iree_string_builder_append_string(builder,
iree_make_cstring_view(value));
}
static iree_status_t iree_string_builder_append_format_impl(
iree_string_builder_t* builder, const char* format, va_list varargs_0,
va_list varargs_1) {
// Try to directly print into the buffer we have. This may work if we have
// capacity but otherwise will yield us the size we need to grow our buffer.
int n = vsnprintf(builder->buffer ? builder->buffer + builder->size : NULL,
builder->buffer ? builder->capacity - builder->size : 0,
format, varargs_0);
if (IREE_UNLIKELY(n < 0)) {
return iree_make_status(IREE_STATUS_INTERNAL, "printf try failed");
}
if (n < builder->capacity - builder->size) {
// Printed into the buffer.
builder->size += n;
return iree_ok_status();
}
if (!iree_string_builder_is_calculating_size(builder)) {
// Reserve new minimum capacity.
IREE_RETURN_IF_ERROR(iree_string_builder_reserve(
builder, iree_string_builder_size(builder) + n + /*NUL*/ 1));
// Try printing again.
vsnprintf(builder->buffer ? builder->buffer + builder->size : NULL,
builder->buffer ? builder->capacity - builder->size : 0, format,
varargs_1);
}
builder->size += n;
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t IREE_PRINTF_ATTRIBUTE(2, 3)
iree_string_builder_append_format(iree_string_builder_t* builder,
const char* format, ...) {
va_list varargs_0, varargs_1;
va_start(varargs_0, format);
va_start(varargs_1, format);
iree_status_t status = iree_string_builder_append_format_impl(
builder, format, varargs_0, varargs_1);
va_end(varargs_1);
va_end(varargs_0);
return status;
}