lkml.org 
[lkml]   [2019]   [May]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    Subject[PATCH v4 03/18] kunit: test: add string_stream a std::stream like string builder
    From
    A number of test features need to do pretty complicated string printing
    where it may not be possible to rely on a single preallocated string
    with parameters.

    So provide a library for constructing the string as you go similar to
    C++'s std::string.

    Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
    Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
    ---
    include/kunit/string-stream.h | 51 ++++++++++++
    kunit/Makefile | 3 +-
    kunit/string-stream.c | 143 ++++++++++++++++++++++++++++++++++
    3 files changed, 196 insertions(+), 1 deletion(-)
    create mode 100644 include/kunit/string-stream.h
    create mode 100644 kunit/string-stream.c

    diff --git a/include/kunit/string-stream.h b/include/kunit/string-stream.h
    new file mode 100644
    index 0000000000000..567a4629406da
    --- /dev/null
    +++ b/include/kunit/string-stream.h
    @@ -0,0 +1,51 @@
    +/* SPDX-License-Identifier: GPL-2.0 */
    +/*
    + * C++ stream style string builder used in KUnit for building messages.
    + *
    + * Copyright (C) 2019, Google LLC.
    + * Author: Brendan Higgins <brendanhiggins@google.com>
    + */
    +
    +#ifndef _KUNIT_STRING_STREAM_H
    +#define _KUNIT_STRING_STREAM_H
    +
    +#include <linux/types.h>
    +#include <linux/spinlock.h>
    +#include <linux/kref.h>
    +#include <stdarg.h>
    +
    +struct string_stream_fragment {
    + struct list_head node;
    + char *fragment;
    +};
    +
    +struct string_stream {
    + size_t length;
    + struct list_head fragments;
    +
    + /* length and fragments are protected by this lock */
    + spinlock_t lock;
    + struct kref refcount;
    +};
    +
    +struct string_stream *new_string_stream(void);
    +
    +void destroy_string_stream(struct string_stream *stream);
    +
    +void string_stream_get(struct string_stream *stream);
    +
    +int string_stream_put(struct string_stream *stream);
    +
    +int string_stream_add(struct string_stream *this, const char *fmt, ...);
    +
    +int string_stream_vadd(struct string_stream *this,
    + const char *fmt,
    + va_list args);
    +
    +char *string_stream_get_string(struct string_stream *this);
    +
    +void string_stream_clear(struct string_stream *this);
    +
    +bool string_stream_is_empty(struct string_stream *this);
    +
    +#endif /* _KUNIT_STRING_STREAM_H */
    diff --git a/kunit/Makefile b/kunit/Makefile
    index 5efdc4dea2c08..275b565a0e81f 100644
    --- a/kunit/Makefile
    +++ b/kunit/Makefile
    @@ -1 +1,2 @@
    -obj-$(CONFIG_KUNIT) += test.o
    +obj-$(CONFIG_KUNIT) += test.o \
    + string-stream.o
    diff --git a/kunit/string-stream.c b/kunit/string-stream.c
    new file mode 100644
    index 0000000000000..d4e4252dc10b8
    --- /dev/null
    +++ b/kunit/string-stream.c
    @@ -0,0 +1,143 @@
    +// SPDX-License-Identifier: GPL-2.0
    +/*
    + * C++ stream style string builder used in KUnit for building messages.
    + *
    + * Copyright (C) 2019, Google LLC.
    + * Author: Brendan Higgins <brendanhiggins@google.com>
    + */
    +
    +#include <linux/list.h>
    +#include <linux/slab.h>
    +#include <kunit/string-stream.h>
    +
    +int string_stream_vadd(struct string_stream *this,
    + const char *fmt,
    + va_list args)
    +{
    + struct string_stream_fragment *frag_container;
    + int len;
    + va_list args_for_counting;
    + unsigned long flags;
    +
    + /* Make a copy because `vsnprintf` could change it */
    + va_copy(args_for_counting, args);
    +
    + /* Need space for null byte. */
    + len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1;
    +
    + va_end(args_for_counting);
    +
    + frag_container = kmalloc(sizeof(*frag_container), GFP_KERNEL);
    + if (!frag_container)
    + return -ENOMEM;
    +
    + frag_container->fragment = kmalloc(len, GFP_KERNEL);
    + if (!frag_container->fragment) {
    + kfree(frag_container);
    + return -ENOMEM;
    + }
    +
    + len = vsnprintf(frag_container->fragment, len, fmt, args);
    + spin_lock_irqsave(&this->lock, flags);
    + this->length += len;
    + list_add_tail(&frag_container->node, &this->fragments);
    + spin_unlock_irqrestore(&this->lock, flags);
    + return 0;
    +}
    +
    +int string_stream_add(struct string_stream *this, const char *fmt, ...)
    +{
    + va_list args;
    + int result;
    +
    + va_start(args, fmt);
    + result = string_stream_vadd(this, fmt, args);
    + va_end(args);
    + return result;
    +}
    +
    +void string_stream_clear(struct string_stream *this)
    +{
    + struct string_stream_fragment *frag_container, *frag_container_safe;
    + unsigned long flags;
    +
    + spin_lock_irqsave(&this->lock, flags);
    + list_for_each_entry_safe(frag_container,
    + frag_container_safe,
    + &this->fragments,
    + node) {
    + list_del(&frag_container->node);
    + kfree(frag_container->fragment);
    + kfree(frag_container);
    + }
    + this->length = 0;
    + spin_unlock_irqrestore(&this->lock, flags);
    +}
    +
    +char *string_stream_get_string(struct string_stream *this)
    +{
    + struct string_stream_fragment *frag_container;
    + size_t buf_len = this->length + 1; /* +1 for null byte. */
    + char *buf;
    + unsigned long flags;
    +
    + buf = kzalloc(buf_len, GFP_KERNEL);
    + if (!buf)
    + return NULL;
    +
    + spin_lock_irqsave(&this->lock, flags);
    + list_for_each_entry(frag_container, &this->fragments, node)
    + strlcat(buf, frag_container->fragment, buf_len);
    + spin_unlock_irqrestore(&this->lock, flags);
    +
    + return buf;
    +}
    +
    +bool string_stream_is_empty(struct string_stream *this)
    +{
    + bool is_empty;
    + unsigned long flags;
    +
    + spin_lock_irqsave(&this->lock, flags);
    + is_empty = list_empty(&this->fragments);
    + spin_unlock_irqrestore(&this->lock, flags);
    +
    + return is_empty;
    +}
    +
    +void destroy_string_stream(struct string_stream *stream)
    +{
    + string_stream_clear(stream);
    + kfree(stream);
    +}
    +
    +static void string_stream_destroy(struct kref *kref)
    +{
    + struct string_stream *stream = container_of(kref,
    + struct string_stream,
    + refcount);
    + destroy_string_stream(stream);
    +}
    +
    +struct string_stream *new_string_stream(void)
    +{
    + struct string_stream *stream = kzalloc(sizeof(*stream), GFP_KERNEL);
    +
    + if (!stream)
    + return NULL;
    +
    + INIT_LIST_HEAD(&stream->fragments);
    + spin_lock_init(&stream->lock);
    + kref_init(&stream->refcount);
    + return stream;
    +}
    +
    +void string_stream_get(struct string_stream *stream)
    +{
    + kref_get(&stream->refcount);
    +}
    +
    +int string_stream_put(struct string_stream *stream)
    +{
    + return kref_put(&stream->refcount, &string_stream_destroy);
    +}
    --
    2.21.0.1020.gf2820cf01a-goog
    \
     
     \ /
      Last update: 2019-05-15 00:18    [W:4.447 / U:0.460 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site