Linux-libre 5.7.6-gnu
[librecmc/linux-libre.git] / lib / kunit / string-stream.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * C++ stream style string builder used in KUnit for building messages.
4  *
5  * Copyright (C) 2019, Google LLC.
6  * Author: Brendan Higgins <brendanhiggins@google.com>
7  */
8
9 #include <kunit/test.h>
10 #include <linux/list.h>
11 #include <linux/slab.h>
12
13 #include "string-stream.h"
14
15 struct string_stream_fragment_alloc_context {
16         struct kunit *test;
17         int len;
18         gfp_t gfp;
19 };
20
21 static int string_stream_fragment_init(struct kunit_resource *res,
22                                        void *context)
23 {
24         struct string_stream_fragment_alloc_context *ctx = context;
25         struct string_stream_fragment *frag;
26
27         frag = kunit_kzalloc(ctx->test, sizeof(*frag), ctx->gfp);
28         if (!frag)
29                 return -ENOMEM;
30
31         frag->test = ctx->test;
32         frag->fragment = kunit_kmalloc(ctx->test, ctx->len, ctx->gfp);
33         if (!frag->fragment)
34                 return -ENOMEM;
35
36         res->allocation = frag;
37
38         return 0;
39 }
40
41 static void string_stream_fragment_free(struct kunit_resource *res)
42 {
43         struct string_stream_fragment *frag = res->allocation;
44
45         list_del(&frag->node);
46         kunit_kfree(frag->test, frag->fragment);
47         kunit_kfree(frag->test, frag);
48 }
49
50 static struct string_stream_fragment *alloc_string_stream_fragment(
51                 struct kunit *test, int len, gfp_t gfp)
52 {
53         struct string_stream_fragment_alloc_context context = {
54                 .test = test,
55                 .len = len,
56                 .gfp = gfp
57         };
58
59         return kunit_alloc_resource(test,
60                                     string_stream_fragment_init,
61                                     string_stream_fragment_free,
62                                     gfp,
63                                     &context);
64 }
65
66 static int string_stream_fragment_destroy(struct string_stream_fragment *frag)
67 {
68         return kunit_resource_destroy(frag->test,
69                                       kunit_resource_instance_match,
70                                       string_stream_fragment_free,
71                                       frag);
72 }
73
74 int string_stream_vadd(struct string_stream *stream,
75                        const char *fmt,
76                        va_list args)
77 {
78         struct string_stream_fragment *frag_container;
79         int len;
80         va_list args_for_counting;
81
82         /* Make a copy because `vsnprintf` could change it */
83         va_copy(args_for_counting, args);
84
85         /* Need space for null byte. */
86         len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1;
87
88         va_end(args_for_counting);
89
90         frag_container = alloc_string_stream_fragment(stream->test,
91                                                       len,
92                                                       stream->gfp);
93         if (!frag_container)
94                 return -ENOMEM;
95
96         len = vsnprintf(frag_container->fragment, len, fmt, args);
97         spin_lock(&stream->lock);
98         stream->length += len;
99         list_add_tail(&frag_container->node, &stream->fragments);
100         spin_unlock(&stream->lock);
101
102         return 0;
103 }
104
105 int string_stream_add(struct string_stream *stream, const char *fmt, ...)
106 {
107         va_list args;
108         int result;
109
110         va_start(args, fmt);
111         result = string_stream_vadd(stream, fmt, args);
112         va_end(args);
113
114         return result;
115 }
116
117 static void string_stream_clear(struct string_stream *stream)
118 {
119         struct string_stream_fragment *frag_container, *frag_container_safe;
120
121         spin_lock(&stream->lock);
122         list_for_each_entry_safe(frag_container,
123                                  frag_container_safe,
124                                  &stream->fragments,
125                                  node) {
126                 string_stream_fragment_destroy(frag_container);
127         }
128         stream->length = 0;
129         spin_unlock(&stream->lock);
130 }
131
132 char *string_stream_get_string(struct string_stream *stream)
133 {
134         struct string_stream_fragment *frag_container;
135         size_t buf_len = stream->length + 1; /* +1 for null byte. */
136         char *buf;
137
138         buf = kunit_kzalloc(stream->test, buf_len, stream->gfp);
139         if (!buf)
140                 return NULL;
141
142         spin_lock(&stream->lock);
143         list_for_each_entry(frag_container, &stream->fragments, node)
144                 strlcat(buf, frag_container->fragment, buf_len);
145         spin_unlock(&stream->lock);
146
147         return buf;
148 }
149
150 int string_stream_append(struct string_stream *stream,
151                          struct string_stream *other)
152 {
153         const char *other_content;
154
155         other_content = string_stream_get_string(other);
156
157         if (!other_content)
158                 return -ENOMEM;
159
160         return string_stream_add(stream, other_content);
161 }
162
163 bool string_stream_is_empty(struct string_stream *stream)
164 {
165         return list_empty(&stream->fragments);
166 }
167
168 struct string_stream_alloc_context {
169         struct kunit *test;
170         gfp_t gfp;
171 };
172
173 static int string_stream_init(struct kunit_resource *res, void *context)
174 {
175         struct string_stream *stream;
176         struct string_stream_alloc_context *ctx = context;
177
178         stream = kunit_kzalloc(ctx->test, sizeof(*stream), ctx->gfp);
179         if (!stream)
180                 return -ENOMEM;
181
182         res->allocation = stream;
183         stream->gfp = ctx->gfp;
184         stream->test = ctx->test;
185         INIT_LIST_HEAD(&stream->fragments);
186         spin_lock_init(&stream->lock);
187
188         return 0;
189 }
190
191 static void string_stream_free(struct kunit_resource *res)
192 {
193         struct string_stream *stream = res->allocation;
194
195         string_stream_clear(stream);
196 }
197
198 struct string_stream *alloc_string_stream(struct kunit *test, gfp_t gfp)
199 {
200         struct string_stream_alloc_context context = {
201                 .test = test,
202                 .gfp = gfp
203         };
204
205         return kunit_alloc_resource(test,
206                                     string_stream_init,
207                                     string_stream_free,
208                                     gfp,
209                                     &context);
210 }
211
212 int string_stream_destroy(struct string_stream *stream)
213 {
214         return kunit_resource_destroy(stream->test,
215                                       kunit_resource_instance_match,
216                                       string_stream_free,
217                                       stream);
218 }