2019-09-23 02:02:33 -07: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 >
*/
2023-08-28 11:41:09 +01:00
# include <kunit/static_stub.h>
2019-09-23 02:02:33 -07:00
# include <kunit/test.h>
# include <linux/list.h>
# include <linux/slab.h>
2020-01-06 22:28:18 +00:00
# include "string-stream.h"
2019-09-23 02:02:33 -07:00
2023-08-28 11:41:08 +01:00
static struct string_stream_fragment * alloc_string_stream_fragment ( int len , gfp_t gfp )
2019-09-23 02:02:33 -07:00
{
struct string_stream_fragment * frag ;
2023-08-28 11:41:08 +01:00
frag = kzalloc ( sizeof ( * frag ) , gfp ) ;
2019-09-23 02:02:33 -07:00
if ( ! frag )
2022-07-22 17:15:30 +00:00
return ERR_PTR ( - ENOMEM ) ;
2019-09-23 02:02:33 -07:00
2023-08-28 11:41:08 +01:00
frag - > fragment = kmalloc ( len , gfp ) ;
2022-10-28 07:42:41 -07:00
if ( ! frag - > fragment ) {
2023-08-28 11:41:08 +01:00
kfree ( frag ) ;
2022-07-22 17:15:30 +00:00
return ERR_PTR ( - ENOMEM ) ;
2022-10-28 07:42:41 -07:00
}
2019-09-23 02:02:33 -07:00
2022-07-22 17:15:30 +00:00
return frag ;
2019-09-23 02:02:33 -07:00
}
2023-08-28 11:41:08 +01:00
static void string_stream_fragment_destroy ( struct string_stream_fragment * frag )
2019-09-23 02:02:33 -07:00
{
list_del ( & frag - > node ) ;
2023-08-28 11:41:08 +01:00
kfree ( frag - > fragment ) ;
kfree ( frag ) ;
2019-09-23 02:02:33 -07:00
}
int string_stream_vadd ( struct string_stream * stream ,
const char * fmt ,
va_list args )
{
struct string_stream_fragment * frag_container ;
2023-08-28 11:41:04 +01:00
int buf_len , result_len ;
2019-09-23 02:02:33 -07:00
va_list args_for_counting ;
/* Make a copy because `vsnprintf` could change it */
va_copy ( args_for_counting , args ) ;
2023-08-28 11:41:02 +01:00
/* Evaluate length of formatted string */
2023-08-28 11:41:04 +01:00
buf_len = vsnprintf ( NULL , 0 , fmt , args_for_counting ) ;
2019-09-23 02:02:33 -07:00
va_end ( args_for_counting ) ;
2023-08-28 11:41:04 +01:00
if ( buf_len = = 0 )
2023-08-28 11:41:02 +01:00
return 0 ;
2023-08-28 11:41:04 +01:00
/* Reserve one extra for possible appended newline. */
if ( stream - > append_newlines )
buf_len + + ;
2023-08-28 11:41:02 +01:00
/* Need space for null byte. */
2023-08-28 11:41:04 +01:00
buf_len + + ;
2023-08-28 11:41:02 +01:00
2023-08-28 11:41:08 +01:00
frag_container = alloc_string_stream_fragment ( buf_len , stream - > gfp ) ;
2022-10-14 12:37:25 +03:00
if ( IS_ERR ( frag_container ) )
return PTR_ERR ( frag_container ) ;
2019-09-23 02:02:33 -07:00
2023-08-28 11:41:04 +01:00
if ( stream - > append_newlines ) {
/* Don't include reserved newline byte in writeable length. */
result_len = vsnprintf ( frag_container - > fragment , buf_len - 1 , fmt , args ) ;
/* Append newline if necessary. */
if ( frag_container - > fragment [ result_len - 1 ] ! = ' \n ' )
result_len = strlcat ( frag_container - > fragment , " \n " , buf_len ) ;
} else {
result_len = vsnprintf ( frag_container - > fragment , buf_len , fmt , args ) ;
}
2019-09-23 02:02:33 -07:00
spin_lock ( & stream - > lock ) ;
2023-08-28 11:41:04 +01:00
stream - > length + = result_len ;
2019-09-23 02:02:33 -07:00
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 ;
}
2023-08-28 11:41:08 +01:00
void string_stream_clear ( struct string_stream * stream )
2019-09-23 02:02:33 -07:00
{
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 ) {
2023-08-28 11:41:08 +01:00
string_stream_fragment_destroy ( frag_container ) ;
2019-09-23 02:02:33 -07: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 ;
2023-08-28 11:41:08 +01:00
buf = kzalloc ( buf_len , stream - > gfp ) ;
2019-09-23 02:02:33 -07:00
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 ;
2023-08-28 11:41:08 +01:00
int ret ;
2019-09-23 02:02:33 -07:00
other_content = string_stream_get_string ( other ) ;
if ( ! other_content )
return - ENOMEM ;
2023-08-28 11:41:08 +01:00
ret = string_stream_add ( stream , other_content ) ;
kfree ( other_content ) ;
return ret ;
2019-09-23 02:02:33 -07:00
}
bool string_stream_is_empty ( struct string_stream * stream )
{
return list_empty ( & stream - > fragments ) ;
}
2023-08-28 11:41:08 +01:00
struct string_stream * alloc_string_stream ( gfp_t gfp )
2019-09-23 02:02:33 -07:00
{
struct string_stream * stream ;
2023-08-28 11:41:08 +01:00
stream = kzalloc ( sizeof ( * stream ) , gfp ) ;
2019-09-23 02:02:33 -07:00
if ( ! stream )
2022-07-22 17:15:30 +00:00
return ERR_PTR ( - ENOMEM ) ;
2019-09-23 02:02:33 -07:00
2022-07-22 17:15:30 +00:00
stream - > gfp = gfp ;
2019-09-23 02:02:33 -07:00
INIT_LIST_HEAD ( & stream - > fragments ) ;
spin_lock_init ( & stream - > lock ) ;
2022-07-22 17:15:30 +00:00
return stream ;
2019-09-23 02:02:33 -07:00
}
2022-07-22 17:15:30 +00:00
void string_stream_destroy ( struct string_stream * stream )
2019-09-23 02:02:33 -07:00
{
2023-08-28 11:41:09 +01:00
KUNIT_STATIC_STUB_REDIRECT ( string_stream_destroy , stream ) ;
2023-10-30 10:47:46 +00:00
if ( IS_ERR_OR_NULL ( stream ) )
2023-08-28 11:41:08 +01:00
return ;
2019-09-23 02:02:33 -07:00
string_stream_clear ( stream ) ;
2023-08-28 11:41:08 +01:00
kfree ( stream ) ;
}
static void resource_free_string_stream ( void * p )
{
struct string_stream * stream = p ;
string_stream_destroy ( stream ) ;
2019-09-23 02:02:33 -07:00
}
2023-08-28 11:41:07 +01:00
struct string_stream * kunit_alloc_string_stream ( struct kunit * test , gfp_t gfp )
{
2023-08-28 11:41:08 +01:00
struct string_stream * stream ;
stream = alloc_string_stream ( gfp ) ;
if ( IS_ERR ( stream ) )
return stream ;
if ( kunit_add_action_or_reset ( test , resource_free_string_stream , stream ) ! = 0 )
return ERR_PTR ( - ENOMEM ) ;
return stream ;
2023-08-28 11:41:07 +01:00
}
void kunit_free_string_stream ( struct kunit * test , struct string_stream * stream )
{
2023-08-28 11:41:08 +01:00
kunit_release_action ( test , resource_free_string_stream , ( void * ) stream ) ;
2023-08-28 11:41:07 +01:00
}