diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c156b40c53..ba05cc6882 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1081,6 +1081,7 @@ virBitmapToData; # util/virbuffer.h virBufferAdd; +virBufferAddBuffer; virBufferAddChar; virBufferAdjustIndent; virBufferAsprintf; diff --git a/src/util/virbuffer.c b/src/util/virbuffer.c index e94b35d1dc..96a0f16d57 100644 --- a/src/util/virbuffer.c +++ b/src/util/virbuffer.c @@ -162,8 +162,7 @@ virBufferAdd(virBufferPtr buf, const char *str, int len) len = strlen(str); needSize = buf->use + indent + len + 2; - if (needSize > buf->size && - virBufferGrow(buf, needSize - buf->use) < 0) + if (virBufferGrow(buf, needSize - buf->use) < 0) return; memset(&buf->content[buf->use], ' ', indent); @@ -172,6 +171,43 @@ virBufferAdd(virBufferPtr buf, const char *str, int len) buf->content[buf->use] = '\0'; } +/** + * virBufferAddBuffer: + * @buf: the buffer to append to + * @toadd: the buffer to append + * + * Add a buffer into another buffer without need to go through: + * virBufferContentAndReset(), virBufferAdd(). Auto indentation + * is (intentionally) NOT applied! + * + * Moreover, be aware that @toadd is eaten with hair. IOW, the + * @toadd buffer is reset after this. + */ +void +virBufferAddBuffer(virBufferPtr buf, virBufferPtr toadd) +{ + unsigned int needSize; + + if (!buf || !toadd) + return; + + if (buf->error || toadd->error) { + if (!buf->error) + buf->error = toadd->error; + virBufferFreeAndReset(toadd); + return; + } + + needSize = buf->use + toadd->use; + if (virBufferGrow(buf, needSize - buf->use) < 0) + return; + + memcpy(&buf->content[buf->use], toadd->content, toadd->use); + buf->use += toadd->use; + buf->content[buf->use] = '\0'; + virBufferFreeAndReset(toadd); +} + /** * virBufferAddChar: * @buf: the buffer to append to diff --git a/src/util/virbuffer.h b/src/util/virbuffer.h index 90e248d1c6..24e81c766c 100644 --- a/src/util/virbuffer.h +++ b/src/util/virbuffer.h @@ -72,6 +72,7 @@ int virBufferCheckErrorInternal(const virBuffer *buf, __LINE__) unsigned int virBufferUse(const virBuffer *buf); void virBufferAdd(virBufferPtr buf, const char *str, int len); +void virBufferAddBuffer(virBufferPtr buf, virBufferPtr toadd); void virBufferAddChar(virBufferPtr buf, char c); void virBufferAsprintf(virBufferPtr buf, const char *format, ...) ATTRIBUTE_FMT_PRINTF(2, 3); diff --git a/tests/virbuftest.c b/tests/virbuftest.c index 554a8c070a..f964febd27 100644 --- a/tests/virbuftest.c +++ b/tests/virbuftest.c @@ -199,6 +199,117 @@ static int testBufTrim(const void *data ATTRIBUTE_UNUSED) return ret; } +static int testBufAddBuffer(const void *data ATTRIBUTE_UNUSED) +{ + virBuffer buf1 = VIR_BUFFER_INITIALIZER; + virBuffer buf2 = VIR_BUFFER_INITIALIZER; + virBuffer buf3 = VIR_BUFFER_INITIALIZER; + int ret = -1; + char *result = NULL; + const char *expected = \ +" A long time ago, in a galaxy far,\n" \ +" far away...\n" \ +" It is a period of civil war.\n" \ +" Rebel spaceships, striking\n" \ +" from a hidden base, have won\n" \ +" their first victory against\n" \ +" the evil Galactic Empire.\n" \ +" During the battle, rebel\n" \ +" spies managed to steal secret\n" \ +" plans to the Empire's\n" \ +" ultimate weapon, the DEATH\n" \ +" STAR, an armored space\n" \ +" station with enough power to\n" \ +" destroy an entire planet.\n"; + + if (virBufferUse(&buf1)) { + TEST_ERROR("buf1 already in use"); + goto cleanup; + } + + if (virBufferUse(&buf2)) { + TEST_ERROR("buf2 already in use"); + goto cleanup; + } + + if (virBufferUse(&buf3)) { + TEST_ERROR("buf3 already in use"); + goto cleanup; + } + + virBufferAdjustIndent(&buf1, 2); + virBufferAddLit(&buf1, "A long time ago, in a galaxy far,\n"); + virBufferAddLit(&buf1, "far away...\n"); + + virBufferAdjustIndent(&buf2, 4); + virBufferAddLit(&buf2, "It is a period of civil war.\n"); + virBufferAddLit(&buf2, "Rebel spaceships, striking\n"); + virBufferAddLit(&buf2, "from a hidden base, have won\n"); + virBufferAddLit(&buf2, "their first victory against\n"); + virBufferAddLit(&buf2, "the evil Galactic Empire.\n"); + + virBufferAdjustIndent(&buf3, 2); + virBufferAddLit(&buf3, "During the battle, rebel\n"); + virBufferAddLit(&buf3, "spies managed to steal secret\n"); + virBufferAddLit(&buf3, "plans to the Empire's\n"); + virBufferAddLit(&buf3, "ultimate weapon, the DEATH\n"); + virBufferAddLit(&buf3, "STAR, an armored space\n"); + virBufferAddLit(&buf3, "station with enough power to\n"); + virBufferAddLit(&buf3, "destroy an entire planet.\n"); + + if (!virBufferUse(&buf1)) { + TEST_ERROR("Error adding to buf1"); + goto cleanup; + } + + if (!virBufferUse(&buf2)) { + TEST_ERROR("Error adding to buf2"); + goto cleanup; + } + + if (!virBufferUse(&buf3)) { + TEST_ERROR("Error adding to buf3"); + goto cleanup; + } + + virBufferAddBuffer(&buf2, &buf3); + + if (!virBufferUse(&buf2)) { + TEST_ERROR("buf2 cleared mistakenly"); + goto cleanup; + } + + if (virBufferUse(&buf3)) { + TEST_ERROR("buf3 is not clear even though it should be"); + goto cleanup; + } + + virBufferAddBuffer(&buf1, &buf2); + + if (!virBufferUse(&buf1)) { + TEST_ERROR("buf1 cleared mistakenly"); + goto cleanup; + } + + if (virBufferUse(&buf2)) { + TEST_ERROR("buf2 is not clear even though it should be"); + goto cleanup; + } + + result = virBufferContentAndReset(&buf1); + if (STRNEQ_NULLABLE(result, expected)) { + virtTestDifference(stderr, expected, result); + goto cleanup; + } + + ret = 0; + cleanup: + virBufferFreeAndReset(&buf1); + virBufferFreeAndReset(&buf2); + VIR_FREE(result); + return ret; +} + static int mymain(void) @@ -217,6 +328,7 @@ mymain(void) DO_TEST("VSprintf infinite loop", testBufInfiniteLoop, 0); DO_TEST("Auto-indentation", testBufAutoIndent, 0); DO_TEST("Trim", testBufTrim, 0); + DO_TEST("AddBuffer", testBufAddBuffer, 0); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }