18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * C++ stream style string builder used in KUnit for building messages. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019, Google LLC. 68c2ecf20Sopenharmony_ci * Author: Brendan Higgins <brendanhiggins@google.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <kunit/test.h> 108c2ecf20Sopenharmony_ci#include <linux/list.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "string-stream.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct string_stream_fragment_alloc_context { 168c2ecf20Sopenharmony_ci struct kunit *test; 178c2ecf20Sopenharmony_ci int len; 188c2ecf20Sopenharmony_ci gfp_t gfp; 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int string_stream_fragment_init(struct kunit_resource *res, 228c2ecf20Sopenharmony_ci void *context) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct string_stream_fragment_alloc_context *ctx = context; 258c2ecf20Sopenharmony_ci struct string_stream_fragment *frag; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci frag = kunit_kzalloc(ctx->test, sizeof(*frag), ctx->gfp); 288c2ecf20Sopenharmony_ci if (!frag) 298c2ecf20Sopenharmony_ci return -ENOMEM; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci frag->test = ctx->test; 328c2ecf20Sopenharmony_ci frag->fragment = kunit_kmalloc(ctx->test, ctx->len, ctx->gfp); 338c2ecf20Sopenharmony_ci if (!frag->fragment) 348c2ecf20Sopenharmony_ci return -ENOMEM; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci res->data = frag; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void string_stream_fragment_free(struct kunit_resource *res) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct string_stream_fragment *frag = res->data; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci list_del(&frag->node); 468c2ecf20Sopenharmony_ci kunit_kfree(frag->test, frag->fragment); 478c2ecf20Sopenharmony_ci kunit_kfree(frag->test, frag); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct string_stream_fragment *alloc_string_stream_fragment( 518c2ecf20Sopenharmony_ci struct kunit *test, int len, gfp_t gfp) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct string_stream_fragment_alloc_context context = { 548c2ecf20Sopenharmony_ci .test = test, 558c2ecf20Sopenharmony_ci .len = len, 568c2ecf20Sopenharmony_ci .gfp = gfp 578c2ecf20Sopenharmony_ci }; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return kunit_alloc_resource(test, 608c2ecf20Sopenharmony_ci string_stream_fragment_init, 618c2ecf20Sopenharmony_ci string_stream_fragment_free, 628c2ecf20Sopenharmony_ci gfp, 638c2ecf20Sopenharmony_ci &context); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int string_stream_fragment_destroy(struct string_stream_fragment *frag) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci return kunit_destroy_resource(frag->test, 698c2ecf20Sopenharmony_ci kunit_resource_instance_match, 708c2ecf20Sopenharmony_ci frag); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciint string_stream_vadd(struct string_stream *stream, 748c2ecf20Sopenharmony_ci const char *fmt, 758c2ecf20Sopenharmony_ci va_list args) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct string_stream_fragment *frag_container; 788c2ecf20Sopenharmony_ci int len; 798c2ecf20Sopenharmony_ci va_list args_for_counting; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Make a copy because `vsnprintf` could change it */ 828c2ecf20Sopenharmony_ci va_copy(args_for_counting, args); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Need space for null byte. */ 858c2ecf20Sopenharmony_ci len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci va_end(args_for_counting); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci frag_container = alloc_string_stream_fragment(stream->test, 908c2ecf20Sopenharmony_ci len, 918c2ecf20Sopenharmony_ci stream->gfp); 928c2ecf20Sopenharmony_ci if (!frag_container) 938c2ecf20Sopenharmony_ci return -ENOMEM; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci len = vsnprintf(frag_container->fragment, len, fmt, args); 968c2ecf20Sopenharmony_ci spin_lock(&stream->lock); 978c2ecf20Sopenharmony_ci stream->length += len; 988c2ecf20Sopenharmony_ci list_add_tail(&frag_container->node, &stream->fragments); 998c2ecf20Sopenharmony_ci spin_unlock(&stream->lock); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ciint string_stream_add(struct string_stream *stream, const char *fmt, ...) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci va_list args; 1078c2ecf20Sopenharmony_ci int result; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci va_start(args, fmt); 1108c2ecf20Sopenharmony_ci result = string_stream_vadd(stream, fmt, args); 1118c2ecf20Sopenharmony_ci va_end(args); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return result; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void string_stream_clear(struct string_stream *stream) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct string_stream_fragment *frag_container, *frag_container_safe; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci spin_lock(&stream->lock); 1218c2ecf20Sopenharmony_ci list_for_each_entry_safe(frag_container, 1228c2ecf20Sopenharmony_ci frag_container_safe, 1238c2ecf20Sopenharmony_ci &stream->fragments, 1248c2ecf20Sopenharmony_ci node) { 1258c2ecf20Sopenharmony_ci string_stream_fragment_destroy(frag_container); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci stream->length = 0; 1288c2ecf20Sopenharmony_ci spin_unlock(&stream->lock); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cichar *string_stream_get_string(struct string_stream *stream) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct string_stream_fragment *frag_container; 1348c2ecf20Sopenharmony_ci size_t buf_len = stream->length + 1; /* +1 for null byte. */ 1358c2ecf20Sopenharmony_ci char *buf; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci buf = kunit_kzalloc(stream->test, buf_len, stream->gfp); 1388c2ecf20Sopenharmony_ci if (!buf) 1398c2ecf20Sopenharmony_ci return NULL; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_lock(&stream->lock); 1428c2ecf20Sopenharmony_ci list_for_each_entry(frag_container, &stream->fragments, node) 1438c2ecf20Sopenharmony_ci strlcat(buf, frag_container->fragment, buf_len); 1448c2ecf20Sopenharmony_ci spin_unlock(&stream->lock); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return buf; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciint string_stream_append(struct string_stream *stream, 1508c2ecf20Sopenharmony_ci struct string_stream *other) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci const char *other_content; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci other_content = string_stream_get_string(other); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!other_content) 1578c2ecf20Sopenharmony_ci return -ENOMEM; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return string_stream_add(stream, other_content); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cibool string_stream_is_empty(struct string_stream *stream) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci return list_empty(&stream->fragments); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistruct string_stream_alloc_context { 1688c2ecf20Sopenharmony_ci struct kunit *test; 1698c2ecf20Sopenharmony_ci gfp_t gfp; 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int string_stream_init(struct kunit_resource *res, void *context) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct string_stream *stream; 1758c2ecf20Sopenharmony_ci struct string_stream_alloc_context *ctx = context; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci stream = kunit_kzalloc(ctx->test, sizeof(*stream), ctx->gfp); 1788c2ecf20Sopenharmony_ci if (!stream) 1798c2ecf20Sopenharmony_ci return -ENOMEM; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci res->data = stream; 1828c2ecf20Sopenharmony_ci stream->gfp = ctx->gfp; 1838c2ecf20Sopenharmony_ci stream->test = ctx->test; 1848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&stream->fragments); 1858c2ecf20Sopenharmony_ci spin_lock_init(&stream->lock); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void string_stream_free(struct kunit_resource *res) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct string_stream *stream = res->data; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci string_stream_clear(stream); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistruct string_stream *alloc_string_stream(struct kunit *test, gfp_t gfp) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct string_stream_alloc_context context = { 2008c2ecf20Sopenharmony_ci .test = test, 2018c2ecf20Sopenharmony_ci .gfp = gfp 2028c2ecf20Sopenharmony_ci }; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return kunit_alloc_resource(test, 2058c2ecf20Sopenharmony_ci string_stream_init, 2068c2ecf20Sopenharmony_ci string_stream_free, 2078c2ecf20Sopenharmony_ci gfp, 2088c2ecf20Sopenharmony_ci &context); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciint string_stream_destroy(struct string_stream *stream) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return kunit_destroy_resource(stream->test, 2148c2ecf20Sopenharmony_ci kunit_resource_instance_match, 2158c2ecf20Sopenharmony_ci stream); 2168c2ecf20Sopenharmony_ci} 217