| // 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(); | |
| } |