string-stream.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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 <[email protected]>
  7. */
  8. #include <kunit/string-stream.h>
  9. #include <kunit/test.h>
  10. #include <linux/list.h>
  11. #include <linux/slab.h>
  12. static struct string_stream_fragment *alloc_string_stream_fragment(
  13. struct kunit *test, int len, gfp_t gfp)
  14. {
  15. struct string_stream_fragment *frag;
  16. frag = kunit_kzalloc(test, sizeof(*frag), gfp);
  17. if (!frag)
  18. return ERR_PTR(-ENOMEM);
  19. frag->fragment = kunit_kmalloc(test, len, gfp);
  20. if (!frag->fragment) {
  21. kunit_kfree(test, frag);
  22. return ERR_PTR(-ENOMEM);
  23. }
  24. return frag;
  25. }
  26. static void string_stream_fragment_destroy(struct kunit *test,
  27. struct string_stream_fragment *frag)
  28. {
  29. list_del(&frag->node);
  30. kunit_kfree(test, frag->fragment);
  31. kunit_kfree(test, frag);
  32. }
  33. int string_stream_vadd(struct string_stream *stream,
  34. const char *fmt,
  35. va_list args)
  36. {
  37. struct string_stream_fragment *frag_container;
  38. int len;
  39. va_list args_for_counting;
  40. /* Make a copy because `vsnprintf` could change it */
  41. va_copy(args_for_counting, args);
  42. /* Need space for null byte. */
  43. len = vsnprintf(NULL, 0, fmt, args_for_counting) + 1;
  44. va_end(args_for_counting);
  45. frag_container = alloc_string_stream_fragment(stream->test,
  46. len,
  47. stream->gfp);
  48. if (IS_ERR(frag_container))
  49. return PTR_ERR(frag_container);
  50. len = vsnprintf(frag_container->fragment, len, fmt, args);
  51. spin_lock(&stream->lock);
  52. stream->length += len;
  53. list_add_tail(&frag_container->node, &stream->fragments);
  54. spin_unlock(&stream->lock);
  55. return 0;
  56. }
  57. int string_stream_add(struct string_stream *stream, const char *fmt, ...)
  58. {
  59. va_list args;
  60. int result;
  61. va_start(args, fmt);
  62. result = string_stream_vadd(stream, fmt, args);
  63. va_end(args);
  64. return result;
  65. }
  66. void string_stream_clear(struct string_stream *stream)
  67. {
  68. struct string_stream_fragment *frag_container, *frag_container_safe;
  69. spin_lock(&stream->lock);
  70. list_for_each_entry_safe(frag_container,
  71. frag_container_safe,
  72. &stream->fragments,
  73. node) {
  74. string_stream_fragment_destroy(stream->test, frag_container);
  75. }
  76. stream->length = 0;
  77. spin_unlock(&stream->lock);
  78. }
  79. char *string_stream_get_string(struct string_stream *stream)
  80. {
  81. struct string_stream_fragment *frag_container;
  82. size_t buf_len = stream->length + 1; /* +1 for null byte. */
  83. char *buf;
  84. buf = kunit_kzalloc(stream->test, buf_len, stream->gfp);
  85. if (!buf)
  86. return NULL;
  87. spin_lock(&stream->lock);
  88. list_for_each_entry(frag_container, &stream->fragments, node)
  89. strlcat(buf, frag_container->fragment, buf_len);
  90. spin_unlock(&stream->lock);
  91. return buf;
  92. }
  93. int string_stream_append(struct string_stream *stream,
  94. struct string_stream *other)
  95. {
  96. const char *other_content;
  97. other_content = string_stream_get_string(other);
  98. if (!other_content)
  99. return -ENOMEM;
  100. return string_stream_add(stream, other_content);
  101. }
  102. bool string_stream_is_empty(struct string_stream *stream)
  103. {
  104. return list_empty(&stream->fragments);
  105. }
  106. struct string_stream_alloc_context {
  107. struct kunit *test;
  108. gfp_t gfp;
  109. };
  110. struct string_stream *alloc_string_stream(struct kunit *test, gfp_t gfp)
  111. {
  112. struct string_stream *stream;
  113. stream = kunit_kzalloc(test, sizeof(*stream), gfp);
  114. if (!stream)
  115. return ERR_PTR(-ENOMEM);
  116. stream->gfp = gfp;
  117. stream->test = test;
  118. INIT_LIST_HEAD(&stream->fragments);
  119. spin_lock_init(&stream->lock);
  120. return stream;
  121. }
  122. void string_stream_destroy(struct string_stream *stream)
  123. {
  124. string_stream_clear(stream);
  125. }