Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * C++ stream style string builder used in KUnit for building messages.
0004  *
0005  * Copyright (C) 2019, Google LLC.
0006  * Author: Brendan Higgins <brendanhiggins@google.com>
0007  */
0008 
0009 #include <kunit/test.h>
0010 #include <linux/list.h>
0011 #include <linux/slab.h>
0012 
0013 #include "string-stream.h"
0014 
0015 struct string_stream_fragment_alloc_context {
0016     struct kunit *test;
0017     int len;
0018     gfp_t gfp;
0019 };
0020 
0021 static int string_stream_fragment_init(struct kunit_resource *res,
0022                        void *context)
0023 {
0024     struct string_stream_fragment_alloc_context *ctx = context;
0025     struct string_stream_fragment *frag;
0026 
0027     frag = kunit_kzalloc(ctx->test, sizeof(*frag), ctx->gfp);
0028     if (!frag)
0029         return -ENOMEM;
0030 
0031     frag->test = ctx->test;
0032     frag->fragment = kunit_kmalloc(ctx->test, ctx->len, ctx->gfp);
0033     if (!frag->fragment)
0034         return -ENOMEM;
0035 
0036     res->data = frag;
0037 
0038     return 0;
0039 }
0040 
0041 static void string_stream_fragment_free(struct kunit_resource *res)
0042 {
0043     struct string_stream_fragment *frag = res->data;
0044 
0045     list_del(&frag->node);
0046     kunit_kfree(frag->test, frag->fragment);
0047     kunit_kfree(frag->test, frag);
0048 }
0049 
0050 static struct string_stream_fragment *alloc_string_stream_fragment(
0051         struct kunit *test, int len, gfp_t gfp)
0052 {
0053     struct string_stream_fragment_alloc_context context = {
0054         .test = test,
0055         .len = len,
0056         .gfp = gfp
0057     };
0058 
0059     return kunit_alloc_resource(test,
0060                     string_stream_fragment_init,
0061                     string_stream_fragment_free,
0062                     gfp,
0063                     &context);
0064 }
0065 
0066 static int string_stream_fragment_destroy(struct string_stream_fragment *frag)
0067 {
0068     return kunit_destroy_resource(frag->test,
0069                       kunit_resource_instance_match,
0070                       frag);
0071 }
0072 
0073 int string_stream_vadd(struct string_stream *stream,
0074                const char *fmt,
0075                va_list args)
0076 {
0077     struct string_stream_fragment *frag_container;
0078     int len;
0079     va_list args_for_counting;
0080 
0081     /* Make a copy because `vsnprintf` could change it */
0082     va_copy(args_for_counting, args);
0083 
0084     /* Need space for null byte. */
0085     len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1;
0086 
0087     va_end(args_for_counting);
0088 
0089     frag_container = alloc_string_stream_fragment(stream->test,
0090                               len,
0091                               stream->gfp);
0092     if (!frag_container)
0093         return -ENOMEM;
0094 
0095     len = vsnprintf(frag_container->fragment, len, fmt, args);
0096     spin_lock(&stream->lock);
0097     stream->length += len;
0098     list_add_tail(&frag_container->node, &stream->fragments);
0099     spin_unlock(&stream->lock);
0100 
0101     return 0;
0102 }
0103 
0104 int string_stream_add(struct string_stream *stream, const char *fmt, ...)
0105 {
0106     va_list args;
0107     int result;
0108 
0109     va_start(args, fmt);
0110     result = string_stream_vadd(stream, fmt, args);
0111     va_end(args);
0112 
0113     return result;
0114 }
0115 
0116 static void string_stream_clear(struct string_stream *stream)
0117 {
0118     struct string_stream_fragment *frag_container, *frag_container_safe;
0119 
0120     spin_lock(&stream->lock);
0121     list_for_each_entry_safe(frag_container,
0122                  frag_container_safe,
0123                  &stream->fragments,
0124                  node) {
0125         string_stream_fragment_destroy(frag_container);
0126     }
0127     stream->length = 0;
0128     spin_unlock(&stream->lock);
0129 }
0130 
0131 char *string_stream_get_string(struct string_stream *stream)
0132 {
0133     struct string_stream_fragment *frag_container;
0134     size_t buf_len = stream->length + 1; /* +1 for null byte. */
0135     char *buf;
0136 
0137     buf = kunit_kzalloc(stream->test, buf_len, stream->gfp);
0138     if (!buf)
0139         return NULL;
0140 
0141     spin_lock(&stream->lock);
0142     list_for_each_entry(frag_container, &stream->fragments, node)
0143         strlcat(buf, frag_container->fragment, buf_len);
0144     spin_unlock(&stream->lock);
0145 
0146     return buf;
0147 }
0148 
0149 int string_stream_append(struct string_stream *stream,
0150              struct string_stream *other)
0151 {
0152     const char *other_content;
0153 
0154     other_content = string_stream_get_string(other);
0155 
0156     if (!other_content)
0157         return -ENOMEM;
0158 
0159     return string_stream_add(stream, other_content);
0160 }
0161 
0162 bool string_stream_is_empty(struct string_stream *stream)
0163 {
0164     return list_empty(&stream->fragments);
0165 }
0166 
0167 struct string_stream_alloc_context {
0168     struct kunit *test;
0169     gfp_t gfp;
0170 };
0171 
0172 static int string_stream_init(struct kunit_resource *res, void *context)
0173 {
0174     struct string_stream *stream;
0175     struct string_stream_alloc_context *ctx = context;
0176 
0177     stream = kunit_kzalloc(ctx->test, sizeof(*stream), ctx->gfp);
0178     if (!stream)
0179         return -ENOMEM;
0180 
0181     res->data = stream;
0182     stream->gfp = ctx->gfp;
0183     stream->test = ctx->test;
0184     INIT_LIST_HEAD(&stream->fragments);
0185     spin_lock_init(&stream->lock);
0186 
0187     return 0;
0188 }
0189 
0190 static void string_stream_free(struct kunit_resource *res)
0191 {
0192     struct string_stream *stream = res->data;
0193 
0194     string_stream_clear(stream);
0195 }
0196 
0197 struct string_stream *alloc_string_stream(struct kunit *test, gfp_t gfp)
0198 {
0199     struct string_stream_alloc_context context = {
0200         .test = test,
0201         .gfp = gfp
0202     };
0203 
0204     return kunit_alloc_resource(test,
0205                     string_stream_init,
0206                     string_stream_free,
0207                     gfp,
0208                     &context);
0209 }
0210 
0211 int string_stream_destroy(struct string_stream *stream)
0212 {
0213     return kunit_destroy_resource(stream->test,
0214                       kunit_resource_instance_match,
0215                       stream);
0216 }