xref: /openbmc/linux/lib/kunit/string-stream.c (revision 26ed1d29fc44f3f2f0c396c1392abefac5f0454e)
1d1fadef1SBrendan Higgins // SPDX-License-Identifier: GPL-2.0
2d1fadef1SBrendan Higgins /*
3d1fadef1SBrendan Higgins  * C++ stream style string builder used in KUnit for building messages.
4d1fadef1SBrendan Higgins  *
5d1fadef1SBrendan Higgins  * Copyright (C) 2019, Google LLC.
6d1fadef1SBrendan Higgins  * Author: Brendan Higgins <brendanhiggins@google.com>
7d1fadef1SBrendan Higgins  */
8d1fadef1SBrendan Higgins 
9d1fadef1SBrendan Higgins #include <kunit/test.h>
10d1fadef1SBrendan Higgins #include <linux/list.h>
11d1fadef1SBrendan Higgins #include <linux/slab.h>
12d1fadef1SBrendan Higgins 
13109fb06fSAlan Maguire #include "string-stream.h"
14109fb06fSAlan Maguire 
15d1fadef1SBrendan Higgins 
alloc_string_stream_fragment(struct kunit * test,int len,gfp_t gfp)16d1fadef1SBrendan Higgins static struct string_stream_fragment *alloc_string_stream_fragment(
17d1fadef1SBrendan Higgins 		struct kunit *test, int len, gfp_t gfp)
18d1fadef1SBrendan Higgins {
1978b1c658SDavid Gow 	struct string_stream_fragment *frag;
20d1fadef1SBrendan Higgins 
2178b1c658SDavid Gow 	frag = kunit_kzalloc(test, sizeof(*frag), gfp);
2278b1c658SDavid Gow 	if (!frag)
2378b1c658SDavid Gow 		return ERR_PTR(-ENOMEM);
2478b1c658SDavid Gow 
2578b1c658SDavid Gow 	frag->fragment = kunit_kmalloc(test, len, gfp);
26*93ef8305SYoungJun.park 	if (!frag->fragment) {
27*93ef8305SYoungJun.park 		kunit_kfree(test, frag);
2878b1c658SDavid Gow 		return ERR_PTR(-ENOMEM);
29*93ef8305SYoungJun.park 	}
3078b1c658SDavid Gow 
3178b1c658SDavid Gow 	return frag;
32d1fadef1SBrendan Higgins }
33d1fadef1SBrendan Higgins 
string_stream_fragment_destroy(struct kunit * test,struct string_stream_fragment * frag)344db4598bSDaniel Latypov static void string_stream_fragment_destroy(struct kunit *test,
354db4598bSDaniel Latypov 					   struct string_stream_fragment *frag)
36d1fadef1SBrendan Higgins {
3778b1c658SDavid Gow 	list_del(&frag->node);
384db4598bSDaniel Latypov 	kunit_kfree(test, frag->fragment);
394db4598bSDaniel Latypov 	kunit_kfree(test, frag);
40d1fadef1SBrendan Higgins }
41d1fadef1SBrendan Higgins 
string_stream_vadd(struct string_stream * stream,const char * fmt,va_list args)42d1fadef1SBrendan Higgins int string_stream_vadd(struct string_stream *stream,
43d1fadef1SBrendan Higgins 		       const char *fmt,
44d1fadef1SBrendan Higgins 		       va_list args)
45d1fadef1SBrendan Higgins {
46d1fadef1SBrendan Higgins 	struct string_stream_fragment *frag_container;
47d1fadef1SBrendan Higgins 	int len;
48d1fadef1SBrendan Higgins 	va_list args_for_counting;
49d1fadef1SBrendan Higgins 
50d1fadef1SBrendan Higgins 	/* Make a copy because `vsnprintf` could change it */
51d1fadef1SBrendan Higgins 	va_copy(args_for_counting, args);
52d1fadef1SBrendan Higgins 
53d1fadef1SBrendan Higgins 	/* Need space for null byte. */
54d1fadef1SBrendan Higgins 	len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1;
55d1fadef1SBrendan Higgins 
56d1fadef1SBrendan Higgins 	va_end(args_for_counting);
57d1fadef1SBrendan Higgins 
58d1fadef1SBrendan Higgins 	frag_container = alloc_string_stream_fragment(stream->test,
59d1fadef1SBrendan Higgins 						      len,
60d1fadef1SBrendan Higgins 						      stream->gfp);
6161888776SDan Carpenter 	if (IS_ERR(frag_container))
6261888776SDan Carpenter 		return PTR_ERR(frag_container);
63d1fadef1SBrendan Higgins 
64d1fadef1SBrendan Higgins 	len = vsnprintf(frag_container->fragment, len, fmt, args);
65d1fadef1SBrendan Higgins 	spin_lock(&stream->lock);
66d1fadef1SBrendan Higgins 	stream->length += len;
67d1fadef1SBrendan Higgins 	list_add_tail(&frag_container->node, &stream->fragments);
68d1fadef1SBrendan Higgins 	spin_unlock(&stream->lock);
69d1fadef1SBrendan Higgins 
70d1fadef1SBrendan Higgins 	return 0;
71d1fadef1SBrendan Higgins }
72d1fadef1SBrendan Higgins 
string_stream_add(struct string_stream * stream,const char * fmt,...)73d1fadef1SBrendan Higgins int string_stream_add(struct string_stream *stream, const char *fmt, ...)
74d1fadef1SBrendan Higgins {
75d1fadef1SBrendan Higgins 	va_list args;
76d1fadef1SBrendan Higgins 	int result;
77d1fadef1SBrendan Higgins 
78d1fadef1SBrendan Higgins 	va_start(args, fmt);
79d1fadef1SBrendan Higgins 	result = string_stream_vadd(stream, fmt, args);
80d1fadef1SBrendan Higgins 	va_end(args);
81d1fadef1SBrendan Higgins 
82d1fadef1SBrendan Higgins 	return result;
83d1fadef1SBrendan Higgins }
84d1fadef1SBrendan Higgins 
string_stream_clear(struct string_stream * stream)85d1fadef1SBrendan Higgins static void string_stream_clear(struct string_stream *stream)
86d1fadef1SBrendan Higgins {
87d1fadef1SBrendan Higgins 	struct string_stream_fragment *frag_container, *frag_container_safe;
88d1fadef1SBrendan Higgins 
89d1fadef1SBrendan Higgins 	spin_lock(&stream->lock);
90d1fadef1SBrendan Higgins 	list_for_each_entry_safe(frag_container,
91d1fadef1SBrendan Higgins 				 frag_container_safe,
92d1fadef1SBrendan Higgins 				 &stream->fragments,
93d1fadef1SBrendan Higgins 				 node) {
944db4598bSDaniel Latypov 		string_stream_fragment_destroy(stream->test, frag_container);
95d1fadef1SBrendan Higgins 	}
96d1fadef1SBrendan Higgins 	stream->length = 0;
97d1fadef1SBrendan Higgins 	spin_unlock(&stream->lock);
98d1fadef1SBrendan Higgins }
99d1fadef1SBrendan Higgins 
string_stream_get_string(struct string_stream * stream)100d1fadef1SBrendan Higgins char *string_stream_get_string(struct string_stream *stream)
101d1fadef1SBrendan Higgins {
102d1fadef1SBrendan Higgins 	struct string_stream_fragment *frag_container;
103d1fadef1SBrendan Higgins 	size_t buf_len = stream->length + 1; /* +1 for null byte. */
104d1fadef1SBrendan Higgins 	char *buf;
105d1fadef1SBrendan Higgins 
106d1fadef1SBrendan Higgins 	buf = kunit_kzalloc(stream->test, buf_len, stream->gfp);
107d1fadef1SBrendan Higgins 	if (!buf)
108d1fadef1SBrendan Higgins 		return NULL;
109d1fadef1SBrendan Higgins 
110d1fadef1SBrendan Higgins 	spin_lock(&stream->lock);
111d1fadef1SBrendan Higgins 	list_for_each_entry(frag_container, &stream->fragments, node)
112d1fadef1SBrendan Higgins 		strlcat(buf, frag_container->fragment, buf_len);
113d1fadef1SBrendan Higgins 	spin_unlock(&stream->lock);
114d1fadef1SBrendan Higgins 
115d1fadef1SBrendan Higgins 	return buf;
116d1fadef1SBrendan Higgins }
117d1fadef1SBrendan Higgins 
string_stream_append(struct string_stream * stream,struct string_stream * other)118d1fadef1SBrendan Higgins int string_stream_append(struct string_stream *stream,
119d1fadef1SBrendan Higgins 			 struct string_stream *other)
120d1fadef1SBrendan Higgins {
121d1fadef1SBrendan Higgins 	const char *other_content;
122d1fadef1SBrendan Higgins 
123d1fadef1SBrendan Higgins 	other_content = string_stream_get_string(other);
124d1fadef1SBrendan Higgins 
125d1fadef1SBrendan Higgins 	if (!other_content)
126d1fadef1SBrendan Higgins 		return -ENOMEM;
127d1fadef1SBrendan Higgins 
128d1fadef1SBrendan Higgins 	return string_stream_add(stream, other_content);
129d1fadef1SBrendan Higgins }
130d1fadef1SBrendan Higgins 
string_stream_is_empty(struct string_stream * stream)131d1fadef1SBrendan Higgins bool string_stream_is_empty(struct string_stream *stream)
132d1fadef1SBrendan Higgins {
133d1fadef1SBrendan Higgins 	return list_empty(&stream->fragments);
134d1fadef1SBrendan Higgins }
135d1fadef1SBrendan Higgins 
alloc_string_stream(struct kunit * test,gfp_t gfp)13678b1c658SDavid Gow struct string_stream *alloc_string_stream(struct kunit *test, gfp_t gfp)
137d1fadef1SBrendan Higgins {
138d1fadef1SBrendan Higgins 	struct string_stream *stream;
139d1fadef1SBrendan Higgins 
14078b1c658SDavid Gow 	stream = kunit_kzalloc(test, sizeof(*stream), gfp);
141d1fadef1SBrendan Higgins 	if (!stream)
14278b1c658SDavid Gow 		return ERR_PTR(-ENOMEM);
143d1fadef1SBrendan Higgins 
14478b1c658SDavid Gow 	stream->gfp = gfp;
14578b1c658SDavid Gow 	stream->test = test;
146d1fadef1SBrendan Higgins 	INIT_LIST_HEAD(&stream->fragments);
147d1fadef1SBrendan Higgins 	spin_lock_init(&stream->lock);
148d1fadef1SBrendan Higgins 
14978b1c658SDavid Gow 	return stream;
150d1fadef1SBrendan Higgins }
151d1fadef1SBrendan Higgins 
string_stream_destroy(struct string_stream * stream)15278b1c658SDavid Gow void string_stream_destroy(struct string_stream *stream)
153d1fadef1SBrendan Higgins {
154d1fadef1SBrendan Higgins 	string_stream_clear(stream);
155d1fadef1SBrendan Higgins }
156