blob: a31f53b851ab2a9587bb03aeb11e6018c844c487 [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) {
if (IREE_LIKELY(builder->capacity >= minimum_capacity)) {
// Already at/above the requested minimum capacity.
return iree_ok_status();
}
// If no allocator was provided the builder cannot grow.
if (iree_allocator_is_null(builder->allocator)) {
return iree_make_status(
IREE_STATUS_RESOURCE_EXHAUSTED,
"non-growable builder capacity exceeded (capacity=%" PRIhsz
"; requested>=%" PRIhsz ")",
builder->capacity, minimum_capacity);
}
// Grow by 2x. Note that the current capacity may be zero.
iree_host_size_t new_capacity = iree_max(
builder->capacity * 2,
iree_host_align(minimum_capacity, IREE_STRING_BUILDER_ALIGNMENT));
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_reserve_for_append(
iree_string_builder_t* builder,
iree_host_size_t minimum_additional_capacity, char** out_buffer,
iree_host_size_t* out_capacity) {
iree_host_size_t new_capacity =
builder->size + minimum_additional_capacity + /*NUL=*/1;
IREE_RETURN_IF_ERROR(iree_string_builder_reserve(builder, new_capacity));
*out_buffer = builder->buffer + builder->size;
*out_capacity = builder->capacity - builder->size - /*NUL=*/1;
return iree_ok_status();
}
IREE_API_EXPORT void iree_string_builder_commit_append(
iree_string_builder_t* builder, iree_host_size_t append_size) {
builder->size += append_size;
builder->buffer[builder->size] = 0;
}
IREE_API_EXPORT void iree_string_builder_reset(iree_string_builder_t* builder) {
builder->size = 0;
}
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;
}
IREE_API_EXPORT void iree_string_pair_builder_initialize(
iree_allocator_t allocator, iree_string_pair_builder_t* out_builder) {
memset(out_builder, 0, sizeof(*out_builder));
out_builder->allocator = allocator;
}
IREE_API_EXPORT void iree_string_pair_builder_deinitialize(
iree_string_pair_builder_t* builder) {
for (iree_host_size_t i = 0; i < builder->temp_strings_size; ++i) {
iree_allocator_free(builder->allocator, builder->temp_strings[i]);
}
iree_allocator_free(builder->allocator, builder->pairs);
iree_allocator_free(builder->allocator, builder->temp_strings);
}
IREE_API_EXPORT iree_status_t iree_string_pair_builder_add(
iree_string_pair_builder_t* builder, iree_string_pair_t pair) {
if (builder->pairs_size == builder->pairs_capacity) {
// Resize.
builder->pairs_capacity = iree_max(8, builder->pairs_capacity * 2);
IREE_RETURN_IF_ERROR(iree_allocator_realloc(
builder->allocator, builder->pairs_capacity * sizeof(builder->pairs[0]),
(void**)&builder->pairs));
}
builder->pairs[builder->pairs_size++] = pair;
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t
iree_string_pair_builder_add_int32(iree_string_pair_builder_t* builder,
iree_string_view_t key, int32_t value) {
char temp[32];
snprintf(temp, sizeof(temp), "%d", value);
iree_string_view_t value_string = iree_make_cstring_view(temp);
IREE_RETURN_IF_ERROR(
iree_string_pair_builder_emplace_string(builder, &value_string));
return iree_string_pair_builder_add(builder,
iree_make_string_pair(key, value_string));
}
IREE_API_EXPORT iree_status_t iree_string_pair_builder_emplace_string(
iree_string_pair_builder_t* builder, iree_string_view_t* inout_string) {
if (builder->temp_strings_size == builder->temp_strings_capacity) {
// Resize.
iree_host_size_t new_capacity =
iree_max(8, builder->temp_strings_capacity * 2);
char** realloced = builder->temp_strings;
IREE_RETURN_IF_ERROR(iree_allocator_realloc(
builder->allocator, new_capacity * sizeof(builder->temp_strings[0]),
(void**)&realloced));
builder->temp_strings_capacity = new_capacity;
builder->temp_strings = realloced;
}
char* alloced = NULL;
IREE_RETURN_IF_ERROR(iree_allocator_malloc(
builder->allocator, inout_string->size + 1, (void**)&alloced));
memcpy(alloced, inout_string->data, inout_string->size);
alloced[inout_string->size] = 0;
builder->temp_strings[builder->temp_strings_size++] = alloced;
inout_string->data = alloced;
return iree_ok_status();
}