Line data Source code
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 40 : static int string_stream_fragment_init(struct kunit_resource *res,
22 : void *context)
23 : {
24 40 : struct string_stream_fragment_alloc_context *ctx = context;
25 : struct string_stream_fragment *frag;
26 :
27 80 : frag = kunit_kzalloc(ctx->test, sizeof(*frag), ctx->gfp);
28 40 : if (!frag)
29 : return -ENOMEM;
30 :
31 40 : frag->test = ctx->test;
32 80 : frag->fragment = kunit_kmalloc(ctx->test, ctx->len, ctx->gfp);
33 40 : if (!frag->fragment)
34 : return -ENOMEM;
35 :
36 40 : res->data = frag;
37 :
38 40 : return 0;
39 : }
40 :
41 40 : static void string_stream_fragment_free(struct kunit_resource *res)
42 : {
43 40 : struct string_stream_fragment *frag = res->data;
44 :
45 80 : list_del(&frag->node);
46 40 : kunit_kfree(frag->test, frag->fragment);
47 40 : kunit_kfree(frag->test, frag);
48 40 : }
49 :
50 : static struct string_stream_fragment *alloc_string_stream_fragment(
51 : struct kunit *test, int len, gfp_t gfp)
52 : {
53 40 : struct string_stream_fragment_alloc_context context = {
54 : .test = test,
55 : .len = len,
56 : .gfp = gfp
57 : };
58 :
59 40 : 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 40 : return kunit_destroy_resource(frag->test,
69 : kunit_resource_instance_match,
70 : frag);
71 : }
72 :
73 40 : int string_stream_vadd(struct string_stream *stream,
74 : const char *fmt,
75 : va_list args)
76 : {
77 : struct string_stream_fragment *frag_container;
78 : int len;
79 : va_list args_for_counting;
80 :
81 : /* Make a copy because `vsnprintf` could change it */
82 40 : va_copy(args_for_counting, args);
83 :
84 : /* Need space for null byte. */
85 40 : len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1;
86 :
87 40 : va_end(args_for_counting);
88 :
89 80 : frag_container = alloc_string_stream_fragment(stream->test,
90 : len,
91 : stream->gfp);
92 40 : if (!frag_container)
93 : return -ENOMEM;
94 :
95 40 : len = vsnprintf(frag_container->fragment, len, fmt, args);
96 80 : spin_lock(&stream->lock);
97 40 : stream->length += len;
98 80 : list_add_tail(&frag_container->node, &stream->fragments);
99 80 : spin_unlock(&stream->lock);
100 :
101 40 : return 0;
102 : }
103 :
104 40 : int string_stream_add(struct string_stream *stream, const char *fmt, ...)
105 : {
106 : va_list args;
107 : int result;
108 :
109 40 : va_start(args, fmt);
110 40 : result = string_stream_vadd(stream, fmt, args);
111 40 : va_end(args);
112 :
113 40 : return result;
114 : }
115 :
116 10 : static void string_stream_clear(struct string_stream *stream)
117 : {
118 : struct string_stream_fragment *frag_container, *frag_container_safe;
119 :
120 20 : spin_lock(&stream->lock);
121 50 : list_for_each_entry_safe(frag_container,
122 : frag_container_safe,
123 : &stream->fragments,
124 : node) {
125 40 : string_stream_fragment_destroy(frag_container);
126 : }
127 10 : stream->length = 0;
128 20 : spin_unlock(&stream->lock);
129 10 : }
130 :
131 10 : char *string_stream_get_string(struct string_stream *stream)
132 : {
133 : struct string_stream_fragment *frag_container;
134 10 : size_t buf_len = stream->length + 1; /* +1 for null byte. */
135 : char *buf;
136 :
137 20 : buf = kunit_kzalloc(stream->test, buf_len, stream->gfp);
138 10 : if (!buf)
139 : return NULL;
140 :
141 20 : spin_lock(&stream->lock);
142 50 : list_for_each_entry(frag_container, &stream->fragments, node)
143 40 : strlcat(buf, frag_container->fragment, buf_len);
144 20 : spin_unlock(&stream->lock);
145 :
146 10 : return buf;
147 : }
148 :
149 0 : int string_stream_append(struct string_stream *stream,
150 : struct string_stream *other)
151 : {
152 : const char *other_content;
153 :
154 0 : other_content = string_stream_get_string(other);
155 :
156 0 : if (!other_content)
157 : return -ENOMEM;
158 :
159 0 : return string_stream_add(stream, other_content);
160 : }
161 :
162 10 : bool string_stream_is_empty(struct string_stream *stream)
163 : {
164 20 : return list_empty(&stream->fragments);
165 : }
166 :
167 : struct string_stream_alloc_context {
168 : struct kunit *test;
169 : gfp_t gfp;
170 : };
171 :
172 10 : static int string_stream_init(struct kunit_resource *res, void *context)
173 : {
174 : struct string_stream *stream;
175 10 : struct string_stream_alloc_context *ctx = context;
176 :
177 20 : stream = kunit_kzalloc(ctx->test, sizeof(*stream), ctx->gfp);
178 10 : if (!stream)
179 : return -ENOMEM;
180 :
181 10 : res->data = stream;
182 10 : stream->gfp = ctx->gfp;
183 10 : stream->test = ctx->test;
184 20 : INIT_LIST_HEAD(&stream->fragments);
185 10 : spin_lock_init(&stream->lock);
186 :
187 10 : return 0;
188 : }
189 :
190 10 : static void string_stream_free(struct kunit_resource *res)
191 : {
192 10 : struct string_stream *stream = res->data;
193 :
194 10 : string_stream_clear(stream);
195 10 : }
196 :
197 10 : struct string_stream *alloc_string_stream(struct kunit *test, gfp_t gfp)
198 : {
199 10 : struct string_stream_alloc_context context = {
200 : .test = test,
201 : .gfp = gfp
202 : };
203 :
204 10 : return kunit_alloc_resource(test,
205 : string_stream_init,
206 : string_stream_free,
207 : gfp,
208 : &context);
209 : }
210 :
211 10 : int string_stream_destroy(struct string_stream *stream)
212 : {
213 10 : return kunit_destroy_resource(stream->test,
214 : kunit_resource_instance_match,
215 : stream);
216 : }
|