2019-09-23 12:02:33 +03:00
// 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 <kunit/test.h>
# include <linux/list.h>
# include <linux/slab.h>
2020-01-07 01:28:18 +03:00
# include "string-stream.h"
2019-09-23 12:02:33 +03:00
2022-07-22 20:15:30 +03:00
static struct string_stream_fragment * alloc_string_stream_fragment (
struct kunit * test , int len , gfp_t gfp )
2019-09-23 12:02:33 +03:00
{
struct string_stream_fragment * frag ;
2022-07-22 20:15:30 +03:00
frag = kunit_kzalloc ( test , sizeof ( * frag ) , gfp ) ;
2019-09-23 12:02:33 +03:00
if ( ! frag )
2022-07-22 20:15:30 +03:00
return ERR_PTR ( - ENOMEM ) ;
2019-09-23 12:02:33 +03:00
2022-07-22 20:15:30 +03:00
frag - > fragment = kunit_kmalloc ( test , len , gfp ) ;
2022-10-28 17:42:41 +03:00
if ( ! frag - > fragment ) {
kunit_kfree ( test , frag ) ;
2022-07-22 20:15:30 +03:00
return ERR_PTR ( - ENOMEM ) ;
2022-10-28 17:42:41 +03:00
}
2019-09-23 12:02:33 +03:00
2022-07-22 20:15:30 +03:00
return frag ;
2019-09-23 12:02:33 +03:00
}
2022-07-22 20:15:31 +03:00
static void string_stream_fragment_destroy ( struct kunit * test ,
struct string_stream_fragment * frag )
2019-09-23 12:02:33 +03:00
{
list_del ( & frag - > node ) ;
2022-07-22 20:15:31 +03:00
kunit_kfree ( test , frag - > fragment ) ;
kunit_kfree ( test , frag ) ;
2019-09-23 12:02:33 +03:00
}
int string_stream_vadd ( struct string_stream * stream ,
const char * fmt ,
va_list args )
{
struct string_stream_fragment * frag_container ;
int len ;
va_list args_for_counting ;
/* 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 = alloc_string_stream_fragment ( stream - > test ,
len ,
stream - > gfp ) ;
2022-10-14 12:37:25 +03:00
if ( IS_ERR ( frag_container ) )
return PTR_ERR ( frag_container ) ;
2019-09-23 12:02:33 +03:00
len = vsnprintf ( frag_container - > fragment , len , fmt , args ) ;
spin_lock ( & stream - > lock ) ;
stream - > length + = len ;
list_add_tail ( & frag_container - > node , & stream - > fragments ) ;
spin_unlock ( & stream - > lock ) ;
return 0 ;
}
int string_stream_add ( struct string_stream * stream , const char * fmt , . . . )
{
va_list args ;
int result ;
va_start ( args , fmt ) ;
result = string_stream_vadd ( stream , fmt , args ) ;
va_end ( args ) ;
return result ;
}
static void string_stream_clear ( struct string_stream * stream )
{
struct string_stream_fragment * frag_container , * frag_container_safe ;
spin_lock ( & stream - > lock ) ;
list_for_each_entry_safe ( frag_container ,
frag_container_safe ,
& stream - > fragments ,
node ) {
2022-07-22 20:15:31 +03:00
string_stream_fragment_destroy ( stream - > test , frag_container ) ;
2019-09-23 12:02:33 +03:00
}
stream - > length = 0 ;
spin_unlock ( & stream - > lock ) ;
}
char * string_stream_get_string ( struct string_stream * stream )
{
struct string_stream_fragment * frag_container ;
size_t buf_len = stream - > length + 1 ; /* +1 for null byte. */
char * buf ;
buf = kunit_kzalloc ( stream - > test , buf_len , stream - > gfp ) ;
if ( ! buf )
return NULL ;
spin_lock ( & stream - > lock ) ;
list_for_each_entry ( frag_container , & stream - > fragments , node )
strlcat ( buf , frag_container - > fragment , buf_len ) ;
spin_unlock ( & stream - > lock ) ;
return buf ;
}
int string_stream_append ( struct string_stream * stream ,
struct string_stream * other )
{
const char * other_content ;
other_content = string_stream_get_string ( other ) ;
if ( ! other_content )
return - ENOMEM ;
return string_stream_add ( stream , other_content ) ;
}
bool string_stream_is_empty ( struct string_stream * stream )
{
return list_empty ( & stream - > fragments ) ;
}
2022-07-22 20:15:30 +03:00
struct string_stream * alloc_string_stream ( struct kunit * test , gfp_t gfp )
2019-09-23 12:02:33 +03:00
{
struct string_stream * stream ;
2022-07-22 20:15:30 +03:00
stream = kunit_kzalloc ( test , sizeof ( * stream ) , gfp ) ;
2019-09-23 12:02:33 +03:00
if ( ! stream )
2022-07-22 20:15:30 +03:00
return ERR_PTR ( - ENOMEM ) ;
2019-09-23 12:02:33 +03:00
2022-07-22 20:15:30 +03:00
stream - > gfp = gfp ;
stream - > test = test ;
2019-09-23 12:02:33 +03:00
INIT_LIST_HEAD ( & stream - > fragments ) ;
spin_lock_init ( & stream - > lock ) ;
2022-07-22 20:15:30 +03:00
return stream ;
2019-09-23 12:02:33 +03:00
}
2022-07-22 20:15:30 +03:00
void string_stream_destroy ( struct string_stream * stream )
2019-09-23 12:02:33 +03:00
{
string_stream_clear ( stream ) ;
}