Merge branch 'nate-i50192-buf' into 'master'
See merge request SchedMD/dev/slurm!4208
diff --git a/src/common/pack.c b/src/common/pack.c
index 34a1403..93285c9 100644
--- a/src/common/pack.c
+++ b/src/common/pack.c
@@ -253,6 +253,59 @@
return SLURM_SUCCESS;
}
+extern int buf_append_bytes(buf_t *buf, const void *ptr, size_t bytes)
+{
+ int rc = EINVAL;
+
+ xassert(buf);
+ xassert(buf->magic == BUF_MAGIC);
+
+ if (!bytes)
+ return SLURM_SUCCESS;
+
+ /*
+ * try_grow_buf_remaining() only rejects these when it has to grow, but
+ * the memcpy() below writes through to borrowed memory whether or not
+ * the buffer needs to grow first
+ */
+ if (buf->mmaped || buf->shadow)
+ return EINVAL;
+
+ /*
+ * try_grow_buf_remaining() takes a uint32_t which would silently
+ * truncate bytes while the memcpy() below would still copy all of them
+ */
+ if (bytes > MAX_BUF_SIZE)
+ return ESLURM_DATA_TOO_LARGE;
+
+ if ((rc = try_grow_buf_remaining(buf, bytes)))
+ return rc;
+
+ (void) memcpy((get_buf_data(buf) + get_buf_offset(buf)), ptr, bytes);
+ set_buf_offset(buf, (get_buf_offset(buf) + bytes));
+
+ return SLURM_SUCCESS;
+}
+
+extern int buf_append_str(buf_t *buf, const char *str)
+{
+ int rc;
+ size_t bytes;
+
+ if (!str)
+ return SLURM_SUCCESS;
+
+ bytes = strlen(str);
+
+ if (!bytes)
+ return SLURM_SUCCESS;
+
+ if ((rc = buf_append_bytes(buf, str, bytes)))
+ return rc;
+
+ return SLURM_SUCCESS;
+}
+
/* init_buf - create an empty buffer of the given size */
buf_t *init_buf(uint32_t size)
{
diff --git a/src/common/pack.h b/src/common/pack.h
index 9f3e3d7..e61192d 100644
--- a/src/common/pack.h
+++ b/src/common/pack.h
@@ -74,11 +74,11 @@
bool shadow;
} buf_t;
-#define get_buf_data(__buf) (__buf->head)
-#define get_buf_offset(__buf) (__buf->processed)
-#define set_buf_offset(__buf,__val) (__buf->processed = __val)
-#define remaining_buf(__buf) (__buf->size - __buf->processed)
-#define size_buf(__buf) (__buf->size)
+#define get_buf_data(__buf) ((__buf)->head)
+#define get_buf_offset(__buf) ((__buf)->processed)
+#define set_buf_offset(__buf, __val) ((__buf)->processed = (__val))
+#define remaining_buf(__buf) ((__buf)->size - (__buf)->processed)
+#define size_buf(__buf) ((__buf)->size)
/* Initialize shadow buffer to point at data with size of bytes */
#define SHADOW_BUF_INITIALIZER(data, bytes) \
@@ -132,6 +132,28 @@
* RET SLURM_SUCCESS or error
*/
extern int try_grow_buf_remaining(buf_t *buffer, uint32_t size);
+
+/*
+ * Append a binary-safe chunk of memory to a buf_t, growing it as needed.
+ * The appended data is not NUL-terminated.
+ * IN buf - buffer to append to
+ * IN ptr - pointer to the bytes to append; may be NULL only if bytes is 0
+ * IN bytes - number of bytes to append
+ * RET SLURM_SUCCESS or error (EINVAL / ESLURM_DATA_TOO_LARGE / ENOMEM)
+ */
+extern int buf_append_bytes(buf_t *buf, const void *ptr, size_t bytes);
+
+/*
+ * Append a NUL-terminated string to a buf_t but will not append NUL-terminator
+ * to the buffer.
+ *
+ * IN buf - buffer to append to
+ * IN str - NUL-terminated string to append; NULL is treated as an empty
+ * string (a no-op success)
+ * RET SLURM_SUCCESS or error (EINVAL / ESLURM_DATA_TOO_LARGE / ENOMEM)
+ */
+extern int buf_append_str(buf_t *buf, const char *str);
+
/*
* Extract Buffer head pointer
* NOTE: Use xfer_buf_data() macro instead
diff --git a/src/common/slurm_xlator.h b/src/common/slurm_xlator.h
index 8b04cbe..1273b02 100644
--- a/src/common/slurm_xlator.h
+++ b/src/common/slurm_xlator.h
@@ -401,35 +401,36 @@
#define xsignal_sigset_create slurm_xsignal_sigset_create
/* xstring.[ch] functions */
-#define _xstrcat slurm_xstrcat
-#define _xstrcatat slurm_xstrcatat
-#define _xstrncat slurm_xstrncat
-#define _xstrcatchar slurm_xstrcatchar
-#define _xstrftimecat slurm_xstrftimecat
-#define _xiso8601timecat slurm_xiso8601timecat
-#define _xrfc5424timecat slurm_xrfc5424timecat
-#define _xstrfmtcat slurm_xstrfmtcat
-#define _xstrfmtcatat slurm_xstrfmtcatat
-#define _xmemcat slurm_xmemcat
-#define xstrdup slurm_xstrdup
-#define xstrdup_printf slurm_xstrdup_printf
-#define _xstrdup_vprintf slurm_xstrdup_vprintf
-#define xstrndup slurm_xstrndup
-#define xbasename slurm_xbasename
-#define xdirname slurm_xdirname
-#define _xstrsubstitute slurm_xstrsubstitute
-#define xshort_hostname slurm_xshort_hostname
-#define xstring_is_whitespace slurm_xstring_is_whitespace
-#define xstrtolower slurm_xstrtolower
-#define xstrchr slurm_xstrchr
-#define xstrrchr slurm_xstrrchr
-#define xstrcmp slurm_xstrcmp
-#define xstrncmp slurm_xstrncmp
-#define xstrcasecmp slurm_xstrcasecmp
-#define xstrncasecmp slurm_xstrncasecmp
-#define xstrstr slurm_xstrstr
-#define xstrcasestr slurm_xstrcasestr
-#define xbase64_from_base64url slurm_xbase64_from_base64url
+#define _xstrcat slurm_xstrcat
+#define _xstrcatat slurm_xstrcatat
+#define _xstrncat slurm_xstrncat
+#define _xstrcatchar slurm_xstrcatchar
+#define _xstrftimecat slurm_xstrftimecat
+#define _xiso8601timecat slurm_xiso8601timecat
+#define _xrfc5424timecat slurm_xrfc5424timecat
+#define _xstrfmtcat slurm_xstrfmtcat
+#define _xstrfmtcatat slurm_xstrfmtcatat
+#define _xmemcat slurm_xmemcat
+#define xstrdup slurm_xstrdup
+#define try_xstrndup slurm_try_xstrndup
+#define xstrdup_printf slurm_xstrdup_printf
+#define _xstrdup_vprintf slurm_xstrdup_vprintf
+#define xstrndup slurm_xstrndup
+#define xbasename slurm_xbasename
+#define xdirname slurm_xdirname
+#define _xstrsubstitute slurm_xstrsubstitute
+#define xshort_hostname slurm_xshort_hostname
+#define xstring_is_whitespace slurm_xstring_is_whitespace
+#define xstrtolower slurm_xstrtolower
+#define xstrchr slurm_xstrchr
+#define xstrrchr slurm_xstrrchr
+#define xstrcmp slurm_xstrcmp
+#define xstrncmp slurm_xstrncmp
+#define xstrcasecmp slurm_xstrcasecmp
+#define xstrncasecmp slurm_xstrncasecmp
+#define xstrstr slurm_xstrstr
+#define xstrcasestr slurm_xstrcasestr
+#define xbase64_from_base64url slurm_xbase64_from_base64url
/* xutf.[ch] functions */
#define utf_encoding_scheme_to_string slurm_utf_encoding_scheme_to_string
diff --git a/src/common/xstring.c b/src/common/xstring.c
index 9e27c42..cc14731 100644
--- a/src/common/xstring.c
+++ b/src/common/xstring.c
@@ -67,34 +67,35 @@
* Define slurm-specific aliases for use by plugins, see slurm_xlator.h
* for details.
*/
-strong_alias(_xstrcat, slurm_xstrcat);
-strong_alias(_xstrncat, slurm_xstrncat);
-strong_alias(_xstrncatat, slurm_xstrncatat);
-strong_alias(_xstrcatchar, slurm_xstrcatchar);
-strong_alias(_xstrftimecat, slurm_xstrftimecat);
-strong_alias(_xiso8601timecat, slurm_xiso8601timecat);
-strong_alias(_xrfc5424timecat, slurm_xrfc5424timecat);
-strong_alias(_xstrfmtcat, slurm_xstrfmtcat);
-strong_alias(_xstrfmtcatat, slurm_xstrfmtcatat);
-strong_alias(_xmemcat, slurm_xmemcat);
-strong_alias(xstrdup, slurm_xstrdup);
-strong_alias(xstrdup_printf, slurm_xstrdup_printf);
-strong_alias(_xstrdup_vprintf, slurm_xstrdup_vprintf);
-strong_alias(xstrndup, slurm_xstrndup);
-strong_alias(xbasename, slurm_xbasename);
-strong_alias(xdirname, slurm_xdirname);
-strong_alias(_xstrsubstitute, slurm_xstrsubstitute);
-strong_alias(xshort_hostname, slurm_xshort_hostname);
+strong_alias(_xstrcat, slurm_xstrcat);
+strong_alias(_xstrncat, slurm_xstrncat);
+strong_alias(_xstrncatat, slurm_xstrncatat);
+strong_alias(_xstrcatchar, slurm_xstrcatchar);
+strong_alias(_xstrftimecat, slurm_xstrftimecat);
+strong_alias(_xiso8601timecat, slurm_xiso8601timecat);
+strong_alias(_xrfc5424timecat, slurm_xrfc5424timecat);
+strong_alias(_xstrfmtcat, slurm_xstrfmtcat);
+strong_alias(_xstrfmtcatat, slurm_xstrfmtcatat);
+strong_alias(_xmemcat, slurm_xmemcat);
+strong_alias(xstrdup, slurm_xstrdup);
+strong_alias(xstrdup_printf, slurm_xstrdup_printf);
+strong_alias(_xstrdup_vprintf, slurm_xstrdup_vprintf);
+strong_alias(xstrndup, slurm_xstrndup);
+strong_alias(try_xstrndup, slurm_try_xstrndup);
+strong_alias(xbasename, slurm_xbasename);
+strong_alias(xdirname, slurm_xdirname);
+strong_alias(_xstrsubstitute, slurm_xstrsubstitute);
+strong_alias(xshort_hostname, slurm_xshort_hostname);
strong_alias(xstring_is_whitespace, slurm_xstring_is_whitespace);
-strong_alias(xstrtolower, slurm_xstrtolower);
-strong_alias(xstrchr, slurm_xstrchr);
-strong_alias(xstrrchr, slurm_xstrrchr);
-strong_alias(xstrcmp, slurm_xstrcmp);
-strong_alias(xstrncmp, slurm_xstrncmp);
-strong_alias(xstrcasecmp, slurm_xstrcasecmp);
-strong_alias(xstrncasecmp, slurm_xstrncasecmp);
-strong_alias(xstrstr, slurm_xstrstr);
-strong_alias(xstrcasestr, slurm_xstrcasestr);
+strong_alias(xstrtolower, slurm_xstrtolower);
+strong_alias(xstrchr, slurm_xstrchr);
+strong_alias(xstrrchr, slurm_xstrrchr);
+strong_alias(xstrcmp, slurm_xstrcmp);
+strong_alias(xstrncmp, slurm_xstrncmp);
+strong_alias(xstrcasecmp, slurm_xstrcasecmp);
+strong_alias(xstrncasecmp, slurm_xstrncasecmp);
+strong_alias(xstrstr, slurm_xstrstr);
+strong_alias(xstrcasestr, slurm_xstrcasestr);
strong_alias(xstrtoken, slurm_xstrtoken);
strong_alias(xbase64_from_base64url, slurm_xbase64_from_base64url);
@@ -529,6 +530,26 @@
return result;
}
+extern char *try_xstrndup(const char *str, const size_t n)
+{
+ size_t siz = 0;
+ char *result = NULL;
+
+ if (!str)
+ return NULL;
+
+ siz = strnlen(str, n);
+ result = try_xmalloc(siz + 1);
+
+ if (!result)
+ return NULL;
+
+ (void) memcpy(result, str, siz);
+ result[siz] = '\0';
+
+ return result;
+}
+
/*
** strtol which only reads 'n' number of chars in the str to get the number
*/
diff --git a/src/common/xstring.h b/src/common/xstring.h
index 299c4c2..dc2532f 100644
--- a/src/common/xstring.h
+++ b/src/common/xstring.h
@@ -155,6 +155,9 @@
*/
char *xstrndup(const char *str, size_t n);
+/* xstrndup which uses xmalloc routines but may return NULL on ENOMEM */
+extern char *try_xstrndup(const char *str, const size_t n);
+
/*
** strtol which only reads 'n' number of chars in the str to get the number
*/
diff --git a/testsuite/slurm_unit/common/Makefile.am b/testsuite/slurm_unit/common/Makefile.am
index c9a914a..0bc302a 100644
--- a/testsuite/slurm_unit/common/Makefile.am
+++ b/testsuite/slurm_unit/common/Makefile.am
@@ -20,6 +20,7 @@
MYCFLAGS = @CHECK_CFLAGS@ -Wall
MYCFLAGS += -D_ISO99_SOURCE
TESTS += xhash-test \
+ buf-test \
data-test \
dns-test \
http-test \
@@ -35,6 +36,8 @@
xhash_test_LDADD = $(LDADD) @CHECK_LIBS@
xahash_test_CFLAGS = $(MYCFLAGS)
xahash_test_LDADD = $(LDADD) @CHECK_LIBS@
+buf_test_CFLAGS = $(MYCFLAGS)
+buf_test_LDADD = $(LDADD) @CHECK_LIBS@
data_test_CFLAGS = $(MYCFLAGS)
data_test_LDADD = $(LDADD) @CHECK_LIBS@
dns_test_CFLAGS = $(MYCFLAGS)
diff --git a/testsuite/slurm_unit/common/Makefile.in b/testsuite/slurm_unit/common/Makefile.in
index 200737e..2f1e40e 100644
--- a/testsuite/slurm_unit/common/Makefile.in
+++ b/testsuite/slurm_unit/common/Makefile.in
@@ -93,6 +93,7 @@
check_PROGRAMS = $(am__EXEEXT_3)
TESTS = log-test$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2)
@HAVE_CHECK_TRUE@am__append_1 = xhash-test \
+@HAVE_CHECK_TRUE@ buf-test \
@HAVE_CHECK_TRUE@ data-test \
@HAVE_CHECK_TRUE@ dns-test \
@HAVE_CHECK_TRUE@ http-test \
@@ -169,24 +170,30 @@
$(top_builddir)/slurm/slurm_version.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
-@HAVE_CHECK_TRUE@am__EXEEXT_1 = xhash-test$(EXEEXT) data-test$(EXEEXT) \
-@HAVE_CHECK_TRUE@ dns-test$(EXEEXT) http-test$(EXEEXT) \
-@HAVE_CHECK_TRUE@ sluid-test$(EXEEXT) xbase64-test$(EXEEXT) \
-@HAVE_CHECK_TRUE@ xstring-test$(EXEEXT) \
+@HAVE_CHECK_TRUE@am__EXEEXT_1 = xhash-test$(EXEEXT) buf-test$(EXEEXT) \
+@HAVE_CHECK_TRUE@ data-test$(EXEEXT) dns-test$(EXEEXT) \
+@HAVE_CHECK_TRUE@ http-test$(EXEEXT) sluid-test$(EXEEXT) \
+@HAVE_CHECK_TRUE@ xbase64-test$(EXEEXT) xstring-test$(EXEEXT) \
@HAVE_CHECK_TRUE@ parse_time-test$(EXEEXT) pack-test$(EXEEXT) \
@HAVE_CHECK_TRUE@ reverse_tree-test$(EXEEXT) \
@HAVE_CHECK_TRUE@ xahash-test$(EXEEXT)
@HAVE_CHECK_TRUE@@HAVE_LUA_TRUE@am__EXEEXT_2 = lua-test$(EXEEXT)
am__EXEEXT_3 = log-test$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2)
-data_test_SOURCES = data-test.c
-data_test_OBJECTS = data_test-data-test.$(OBJEXT)
+buf_test_SOURCES = buf-test.c
+buf_test_OBJECTS = buf_test-buf-test.$(OBJEXT)
am__DEPENDENCIES_1 =
am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
-@HAVE_CHECK_TRUE@data_test_DEPENDENCIES = $(am__DEPENDENCIES_2)
+@HAVE_CHECK_TRUE@buf_test_DEPENDENCIES = $(am__DEPENDENCIES_2)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
+buf_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(buf_test_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+data_test_SOURCES = data-test.c
+data_test_OBJECTS = data_test-data-test.$(OBJEXT)
+@HAVE_CHECK_TRUE@data_test_DEPENDENCIES = $(am__DEPENDENCIES_2)
data_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(data_test_CFLAGS) \
$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
@@ -282,7 +289,8 @@
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/slurm
depcomp = $(SHELL) $(top_srcdir)/auxdir/depcomp
am__maybe_remake_depfiles = depfiles
-am__depfiles_remade = ./$(DEPDIR)/data_test-data-test.Po \
+am__depfiles_remade = ./$(DEPDIR)/buf_test-buf-test.Po \
+ ./$(DEPDIR)/data_test-data-test.Po \
./$(DEPDIR)/dns_test-dns-test.Po \
./$(DEPDIR)/http_test-http-test.Po ./$(DEPDIR)/log-test.Po \
./$(DEPDIR)/lua_test-lua-test.Po \
@@ -313,9 +321,10 @@
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
-SOURCES = data-test.c dns-test.c http-test.c log-test.c lua-test.c \
- pack-test.c parse_time-test.c reverse_tree-test.c sluid-test.c \
- xahash-test.c xbase64-test.c xhash-test.c xstring-test.c
+SOURCES = buf-test.c data-test.c dns-test.c http-test.c log-test.c \
+ lua-test.c pack-test.c parse_time-test.c reverse_tree-test.c \
+ sluid-test.c xahash-test.c xbase64-test.c xhash-test.c \
+ xstring-test.c
RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
ctags-recursive dvi-recursive html-recursive info-recursive \
install-data-recursive install-dvi-recursive \
@@ -842,6 +851,8 @@
@HAVE_CHECK_TRUE@xhash_test_LDADD = $(LDADD) @CHECK_LIBS@
@HAVE_CHECK_TRUE@xahash_test_CFLAGS = $(MYCFLAGS)
@HAVE_CHECK_TRUE@xahash_test_LDADD = $(LDADD) @CHECK_LIBS@
+@HAVE_CHECK_TRUE@buf_test_CFLAGS = $(MYCFLAGS)
+@HAVE_CHECK_TRUE@buf_test_LDADD = $(LDADD) @CHECK_LIBS@
@HAVE_CHECK_TRUE@data_test_CFLAGS = $(MYCFLAGS)
@HAVE_CHECK_TRUE@data_test_LDADD = $(LDADD) @CHECK_LIBS@
@HAVE_CHECK_TRUE@dns_test_CFLAGS = $(MYCFLAGS)
@@ -906,6 +917,10 @@
$(am__rm_f) $(check_PROGRAMS)
test -z "$(EXEEXT)" || $(am__rm_f) $(check_PROGRAMS:$(EXEEXT)=)
+buf-test$(EXEEXT): $(buf_test_OBJECTS) $(buf_test_DEPENDENCIES) $(EXTRA_buf_test_DEPENDENCIES)
+ @rm -f buf-test$(EXEEXT)
+ $(AM_V_CCLD)$(buf_test_LINK) $(buf_test_OBJECTS) $(buf_test_LDADD) $(LIBS)
+
data-test$(EXEEXT): $(data_test_OBJECTS) $(data_test_DEPENDENCIES) $(EXTRA_data_test_DEPENDENCIES)
@rm -f data-test$(EXEEXT)
$(AM_V_CCLD)$(data_test_LINK) $(data_test_OBJECTS) $(data_test_LDADD) $(LIBS)
@@ -964,6 +979,7 @@
distclean-compile:
-rm -f *.tab.c
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buf_test-buf-test.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data_test-data-test.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns_test-dns-test.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_test-http-test.Po@am__quote@ # am--include-marker
@@ -1005,6 +1021,20 @@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+buf_test-buf-test.o: buf-test.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buf_test_CFLAGS) $(CFLAGS) -MT buf_test-buf-test.o -MD -MP -MF $(DEPDIR)/buf_test-buf-test.Tpo -c -o buf_test-buf-test.o `test -f 'buf-test.c' || echo '$(srcdir)/'`buf-test.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/buf_test-buf-test.Tpo $(DEPDIR)/buf_test-buf-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buf-test.c' object='buf_test-buf-test.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buf_test_CFLAGS) $(CFLAGS) -c -o buf_test-buf-test.o `test -f 'buf-test.c' || echo '$(srcdir)/'`buf-test.c
+
+buf_test-buf-test.obj: buf-test.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buf_test_CFLAGS) $(CFLAGS) -MT buf_test-buf-test.obj -MD -MP -MF $(DEPDIR)/buf_test-buf-test.Tpo -c -o buf_test-buf-test.obj `if test -f 'buf-test.c'; then $(CYGPATH_W) 'buf-test.c'; else $(CYGPATH_W) '$(srcdir)/buf-test.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/buf_test-buf-test.Tpo $(DEPDIR)/buf_test-buf-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buf-test.c' object='buf_test-buf-test.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(buf_test_CFLAGS) $(CFLAGS) -c -o buf_test-buf-test.obj `if test -f 'buf-test.c'; then $(CYGPATH_W) 'buf-test.c'; else $(CYGPATH_W) '$(srcdir)/buf-test.c'; fi`
+
data_test-data-test.o: data-test.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(data_test_CFLAGS) $(CFLAGS) -MT data_test-data-test.o -MD -MP -MF $(DEPDIR)/data_test-data-test.Tpo -c -o data_test-data-test.o `test -f 'data-test.c' || echo '$(srcdir)/'`data-test.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/data_test-data-test.Tpo $(DEPDIR)/data_test-data-test.Po
@@ -1458,6 +1488,13 @@
--log-file $$b.log --trs-file $$b.trs \
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
"$$tst" $(AM_TESTS_FD_REDIRECT)
+buf-test.log: buf-test$(EXEEXT)
+ @p='buf-test$(EXEEXT)'; \
+ b='buf-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
data-test.log: data-test$(EXEEXT)
@p='data-test$(EXEEXT)'; \
b='data-test'; \
@@ -1595,6 +1632,7 @@
mostlyclean-am
distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/buf_test-buf-test.Po
-rm -f ./$(DEPDIR)/data_test-data-test.Po
-rm -f ./$(DEPDIR)/dns_test-dns-test.Po
-rm -f ./$(DEPDIR)/http_test-http-test.Po
@@ -1653,6 +1691,7 @@
installcheck-am:
maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/buf_test-buf-test.Po
-rm -f ./$(DEPDIR)/data_test-data-test.Po
-rm -f ./$(DEPDIR)/dns_test-dns-test.Po
-rm -f ./$(DEPDIR)/http_test-http-test.Po
diff --git a/testsuite/slurm_unit/common/buf-test.c b/testsuite/slurm_unit/common/buf-test.c
new file mode 100644
index 0000000..325fe1e
--- /dev/null
+++ b/testsuite/slurm_unit/common/buf-test.c
@@ -0,0 +1,613 @@
+/*****************************************************************************\
+ * buf-test.c - unit tests for the buf_t lifecycle and accessors
+ *****************************************************************************
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ *
+ * This file is part of Slurm, a resource management program.
+ * For details, see <https://slurm.schedmd.com/>.
+ * Please also read the included file: DISCLAIMER.
+ *
+ * Slurm is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, as a special exception, the copyright holders give permission
+ * to link the code of portions of this program with the OpenSSL library under
+ * certain conditions as described in each individual source file, and
+ * distribute linked combinations including the two. You must obey the GNU
+ * General Public License in all respects for all of the code used other than
+ * OpenSSL. If you modify file(s) with this exception, you may extend this
+ * exception to your version of the file(s), but you are not obligated to do
+ * so. If you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files in
+ * the program, then also delete it here.
+ *
+ * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with Slurm; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+\*****************************************************************************/
+
+#include <check.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "slurm/slurm_errno.h"
+
+#include "src/common/log.h"
+#include "src/common/pack.h"
+#include "src/common/read_config.h"
+#include "src/common/xmalloc.h"
+
+/*
+ * Tests for the buf_t life cycle and accessors in src/common/pack.c.
+ * The pack*()/unpack*() serializers are covered by pack-test.c.
+ */
+
+/* Verify buffer is empty and sized as requested */
+static void _check_empty_buf(buf_t *buf, const uint32_t size)
+{
+ ck_assert(buf != NULL);
+ ck_assert(get_buf_data(buf) != NULL);
+ ck_assert_int_eq(size_buf(buf), size);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+ ck_assert_int_eq(remaining_buf(buf), size);
+}
+
+START_TEST(test_init_buf)
+{
+ buf_t *buf = init_buf(128);
+ const char zeros[128] = { 0 };
+
+ _check_empty_buf(buf, 128);
+ /* init_buf() uses xmalloc() which always zeros the allocation */
+ ck_assert_msg(!memcmp(get_buf_data(buf), zeros, sizeof(zeros)),
+ "init_buf() did not zero the allocation");
+ free_buf(buf);
+
+ /* a size of zero must fall back to the BUF_SIZE default */
+ buf = init_buf(0);
+ _check_empty_buf(buf, BUF_SIZE);
+ free_buf(buf);
+
+ /* free_buf() must accept NULL */
+ free_buf(NULL);
+
+ buf = init_buf(0);
+ FREE_NULL_BUFFER(buf);
+ ck_assert(buf == NULL);
+ /* FREE_NULL_BUFFER() must be a no-op when already NULL */
+ FREE_NULL_BUFFER(buf);
+ ck_assert(buf == NULL);
+}
+
+END_TEST
+
+START_TEST(test_try_init_buf)
+{
+ buf_t *buf = try_init_buf(128);
+
+ _check_empty_buf(buf, 128);
+ free_buf(buf);
+
+ /* a size of zero must fall back to the BUF_SIZE default */
+ buf = try_init_buf(0);
+ _check_empty_buf(buf, BUF_SIZE);
+ free_buf(buf);
+
+ /* oversized request must be rejected instead of fatal()ing */
+ ck_assert(try_init_buf(MAX_BUF_SIZE + 1) == NULL);
+}
+
+END_TEST
+
+START_TEST(test_create_buf)
+{
+ char *data = xmalloc(16);
+ buf_t *buf;
+
+ memcpy(data, "0123456789abcdef", 16);
+
+ buf = create_buf(data, 16);
+ ck_assert(buf != NULL);
+ /* create_buf() takes ownership of data instead of copying it */
+ ck_assert(get_buf_data(buf) == data);
+ ck_assert_int_eq(size_buf(buf), 16);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+ ck_assert_int_eq(remaining_buf(buf), 16);
+ free_buf(buf);
+
+ /* oversized request must be rejected */
+ ck_assert(create_buf(NULL, MAX_BUF_SIZE + 1) == NULL);
+}
+
+END_TEST
+
+START_TEST(test_create_shadow_buf)
+{
+ char data[] = "shadowed";
+ buf_t *buf = create_shadow_buf(data, (sizeof(data) - 1));
+
+ ck_assert(buf != NULL);
+ ck_assert(get_buf_data(buf) == data);
+ ck_assert_int_eq(size_buf(buf), (sizeof(data) - 1));
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+
+ /* shadow buffers do not own their memory and can not be grown */
+ ck_assert_int_eq(try_grow_buf(buf, 1), EINVAL);
+ ck_assert_int_eq(size_buf(buf), (sizeof(data) - 1));
+
+ /*
+ * try_grow_buf_remaining() only rejects an append when it has to grow,
+ * so with room already remaining an append must still be rejected by
+ * buf_append_bytes()/buf_append_str()'s own shadow check, or it would
+ * write through to the caller's memory
+ */
+ ck_assert_int_eq(buf_append_bytes(buf, "XXXX", 4), EINVAL);
+ ck_assert_int_eq(buf_append_str(buf, "XXXX"), EINVAL);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+ ck_assert_str_eq(data, "shadowed");
+
+ /* free_buf() must not release the shadowed memory */
+ free_buf(buf);
+ ck_assert_str_eq(data, "shadowed");
+
+ ck_assert(create_shadow_buf(data, (MAX_BUF_SIZE + 1)) == NULL);
+}
+
+END_TEST
+
+START_TEST(test_create_mmap_buf)
+{
+ const char contents[] = "mmap()ed buffer contents";
+ const size_t bytes = (sizeof(contents) - 1);
+ char path[PATH_MAX];
+ int fd;
+ buf_t *buf;
+
+ snprintf(path, sizeof(path), "%s/buf-test-XXXXXX",
+ (getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp"));
+ fd = mkstemp(path);
+
+ ck_assert_int_ge(fd, 0);
+ ck_assert_int_eq(write(fd, contents, bytes), bytes);
+ ck_assert_int_eq(close(fd), 0);
+
+ buf = create_mmap_buf(path);
+ /* the mapping outlives the file, so unlink before asserting anything */
+ ck_assert_int_eq(unlink(path), 0);
+
+ ck_assert(buf != NULL);
+ ck_assert_int_eq(size_buf(buf), bytes);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+ ck_assert_int_eq(remaining_buf(buf), bytes);
+ ck_assert_msg(!memcmp(get_buf_data(buf), contents, bytes),
+ "mmap()ed buffer does not match the file contents");
+
+ /* mmap()ed buffers are read only and can not be grown */
+ ck_assert_int_eq(try_grow_buf(buf, 1), EINVAL);
+
+ /*
+ * room remains in the mapping, so only buf_append_bytes()/
+ * buf_append_str()'s own mmaped check stops a write through a
+ * PROT_READ mapping -- try_grow_buf_remaining() alone would not
+ */
+ ck_assert_int_eq(buf_append_bytes(buf, "X", 1), EINVAL);
+ ck_assert_int_eq(buf_append_str(buf, "X"), EINVAL);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+ ck_assert_msg(!memcmp(get_buf_data(buf), contents, bytes),
+ "a rejected append must not modify the mapped contents");
+
+ free_buf(buf);
+
+ /* a file that can not be opened must not be fatal */
+ ck_assert(create_mmap_buf(path) == NULL);
+}
+
+END_TEST
+
+START_TEST(test_shadow_buf_initializer)
+{
+ char data[] = "shadowed";
+ buf_t buf = SHADOW_BUF_INITIALIZER(data, (sizeof(data) - 1));
+
+ ck_assert(get_buf_data(&buf) == data);
+ ck_assert_int_eq(size_buf(&buf), (sizeof(data) - 1));
+ /*
+ * Unlike create_shadow_buf(), the initializer marks the buffer as fully
+ * populated so it is ready to be read back
+ */
+ ck_assert_int_eq(get_buf_offset(&buf), (sizeof(data) - 1));
+ ck_assert_int_eq(remaining_buf(&buf), 0);
+
+ /* the shadowed memory must never be grown or written past */
+ ck_assert_int_eq(try_grow_buf(&buf, 1), EINVAL);
+ ck_assert_int_eq(try_grow_buf_remaining(&buf, 1), EINVAL);
+ ck_assert_int_eq(buf_append_bytes(&buf, "x", 1), EINVAL);
+ ck_assert_int_eq(buf_append_str(&buf, "x"), EINVAL);
+ ck_assert_int_eq(get_buf_offset(&buf), (sizeof(data) - 1));
+ ck_assert_str_eq(data, "shadowed");
+}
+
+END_TEST
+
+START_TEST(test_buf_macros)
+{
+ /*
+ * Every macro argument below is an expression that only parses (or only
+ * evaluates correctly) when the macro parenthesizes its parameters.
+ * These are regression guards for the get_buf_data(), get_buf_offset(),
+ * set_buf_offset(), remaining_buf() and size_buf() macros.
+ */
+ char data[] = "shadowed";
+ buf_t stack_buf = SHADOW_BUF_INITIALIZER(data, (sizeof(data) - 1));
+ buf_t *buf = init_buf(64);
+ buf_t **buf_ptr = &buf;
+ void *ptr = buf;
+
+ ck_assert(get_buf_data((buf_t *) ptr) == get_buf_data(buf));
+ ck_assert_int_eq(get_buf_offset(*buf_ptr), 0);
+
+ set_buf_offset(*buf_ptr, (get_buf_offset(buf) + 8));
+ ck_assert_int_eq(get_buf_offset(buf), 8);
+
+ ck_assert_int_eq(remaining_buf(&stack_buf), 0);
+ ck_assert_int_eq(remaining_buf(buf ? buf : &stack_buf), (64 - 8));
+ ck_assert_int_eq(size_buf(buf ? buf : &stack_buf), 64);
+ ck_assert_int_eq(size_buf(&stack_buf), (sizeof(data) - 1));
+
+ free_buf(buf);
+}
+
+END_TEST
+
+START_TEST(test_grow_buf)
+{
+ buf_t *buf = init_buf(16);
+
+ ck_assert_int_eq(buf_append_str(buf, "0123456789abcdef"),
+ SLURM_SUCCESS);
+ ck_assert_int_eq(remaining_buf(buf), 0);
+
+ /* grow_buf() grows by exactly the requested number of bytes */
+ grow_buf(buf, 16);
+ ck_assert_int_eq(size_buf(buf), 32);
+ ck_assert_int_eq(get_buf_offset(buf), 16);
+ ck_assert_int_eq(remaining_buf(buf), 16);
+ /* existing contents must survive the resize */
+ ck_assert_msg(!memcmp(get_buf_data(buf), "0123456789abcdef", 16),
+ "grow_buf() did not preserve the contents");
+
+ free_buf(buf);
+}
+
+END_TEST
+
+START_TEST(test_try_grow_buf)
+{
+ buf_t *buf = init_buf(16);
+ /* buffer with no memory to avoid allocating MAX_BUF_SIZE bytes */
+ buf_t full_buf = {
+ .magic = BUF_MAGIC,
+ .size = MAX_BUF_SIZE,
+ };
+
+ /*
+ * Growth is always at least BUF_SIZE to avoid a xrealloc() per append,
+ * and requests of BUF_SIZE or larger are added on top of that
+ */
+ ck_assert_int_eq(try_grow_buf(buf, 1), SLURM_SUCCESS);
+ ck_assert_int_eq(size_buf(buf), (16 + BUF_SIZE));
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+
+ ck_assert_int_eq(try_grow_buf(buf, (BUF_SIZE - 1)), SLURM_SUCCESS);
+ ck_assert_int_eq(size_buf(buf), (16 + (2 * BUF_SIZE)));
+
+ ck_assert_int_eq(try_grow_buf(buf, BUF_SIZE), SLURM_SUCCESS);
+ ck_assert_int_eq(size_buf(buf), (16 + (4 * BUF_SIZE)));
+
+ free_buf(buf);
+
+ /* growing past MAX_BUF_SIZE must be rejected without changing size */
+ ck_assert_int_eq(try_grow_buf(&full_buf, 1), ESLURM_DATA_TOO_LARGE);
+ ck_assert_int_eq(size_buf(&full_buf), MAX_BUF_SIZE);
+}
+
+END_TEST
+
+START_TEST(test_try_grow_buf_remaining)
+{
+ buf_t *buf = init_buf(BUF_SIZE);
+ /* buffer with no memory to avoid allocating MAX_BUF_SIZE bytes */
+ buf_t full_buf = {
+ .magic = BUF_MAGIC,
+ .size = MAX_BUF_SIZE,
+ .processed = MAX_BUF_SIZE,
+ };
+
+ /* an exact fit must not grow the buffer */
+ ck_assert_int_eq(try_grow_buf_remaining(buf, BUF_SIZE), SLURM_SUCCESS);
+ ck_assert_int_eq(size_buf(buf), BUF_SIZE);
+
+ set_buf_offset(buf, 1);
+ ck_assert_int_eq(remaining_buf(buf), (BUF_SIZE - 1));
+
+ /* one byte short must grow the buffer */
+ ck_assert_int_eq(try_grow_buf_remaining(buf, BUF_SIZE), SLURM_SUCCESS);
+ ck_assert_int_eq(size_buf(buf), (3 * BUF_SIZE));
+ /* growing must never move the read/write offset */
+ ck_assert_int_eq(get_buf_offset(buf), 1);
+
+ free_buf(buf);
+
+ ck_assert_int_eq(try_grow_buf_remaining(&full_buf, 1),
+ ESLURM_DATA_TOO_LARGE);
+ ck_assert_int_eq(size_buf(&full_buf), MAX_BUF_SIZE);
+}
+
+END_TEST
+
+START_TEST(test_buf_append_bytes)
+{
+ const char bytes[] = { 'a', '\0', 'b', '\0', 'c' };
+ buf_t *buf = init_buf(16);
+
+ /* appending nothing is always a no-op success */
+ ck_assert_int_eq(buf_append_bytes(buf, NULL, 0), SLURM_SUCCESS);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+ ck_assert_int_eq(size_buf(buf), 16);
+
+ /* appending is binary safe */
+ ck_assert_int_eq(buf_append_bytes(buf, bytes, sizeof(bytes)),
+ SLURM_SUCCESS);
+ ck_assert_int_eq(get_buf_offset(buf), sizeof(bytes));
+ ck_assert_int_eq(size_buf(buf), 16);
+ ck_assert_msg(!memcmp(get_buf_data(buf), bytes, sizeof(bytes)),
+ "appended bytes do not match the source");
+
+ /* appends are contiguous */
+ ck_assert_int_eq(buf_append_bytes(buf, bytes, sizeof(bytes)),
+ SLURM_SUCCESS);
+ ck_assert_int_eq(get_buf_offset(buf), (2 * sizeof(bytes)));
+ ck_assert_msg(!memcmp((get_buf_data(buf) + sizeof(bytes)), bytes,
+ sizeof(bytes)),
+ "second append is not contiguous with the first");
+
+ free_buf(buf);
+}
+
+END_TEST
+
+START_TEST(test_buf_append_bytes_grow)
+{
+ char *chunk = xmalloc(BUF_SIZE);
+ buf_t *buf = init_buf(BUF_SIZE);
+
+ memset(chunk, 'a', BUF_SIZE);
+
+ /* an exact fit must not grow the buffer */
+ ck_assert_int_eq(buf_append_bytes(buf, chunk, BUF_SIZE), SLURM_SUCCESS);
+ ck_assert_int_eq(size_buf(buf), BUF_SIZE);
+ ck_assert_int_eq(get_buf_offset(buf), BUF_SIZE);
+ ck_assert_int_eq(remaining_buf(buf), 0);
+
+ /* a full buffer must grow to take another byte */
+ ck_assert_int_eq(buf_append_bytes(buf, "b", 1), SLURM_SUCCESS);
+ ck_assert_int_eq(size_buf(buf), (2 * BUF_SIZE));
+ ck_assert_int_eq(get_buf_offset(buf), (BUF_SIZE + 1));
+ /* contents must survive the implicit grow */
+ ck_assert_msg(!memcmp(get_buf_data(buf), chunk, BUF_SIZE),
+ "buf_append_bytes() did not preserve the contents");
+ ck_assert_int_eq(get_buf_data(buf)[BUF_SIZE], 'b');
+
+ free_buf(buf);
+ xfree(chunk);
+}
+
+END_TEST
+
+START_TEST(test_buf_append_bytes_too_large)
+{
+ /* buffer with no memory to avoid allocating MAX_BUF_SIZE bytes */
+ buf_t full_buf = {
+ .magic = BUF_MAGIC,
+ .size = MAX_BUF_SIZE,
+ .processed = MAX_BUF_SIZE,
+ };
+
+ ck_assert_int_eq(buf_append_bytes(&full_buf, "x", 1),
+ ESLURM_DATA_TOO_LARGE);
+ ck_assert_int_eq(buf_append_str(&full_buf, "x"), ESLURM_DATA_TOO_LARGE);
+ /* a failed append must not advance the offset */
+ ck_assert_int_eq(get_buf_offset(&full_buf), MAX_BUF_SIZE);
+
+ /*
+ * A single request larger than MAX_BUF_SIZE must be rejected outright,
+ * even against a small buffer with room to spare
+ */
+ {
+ buf_t *buf = init_buf(16);
+
+ ck_assert_int_eq(buf_append_bytes(buf, NULL,
+ ((size_t) MAX_BUF_SIZE) + 1),
+ ESLURM_DATA_TOO_LARGE);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+ ck_assert_int_eq(size_buf(buf), 16);
+
+ free_buf(buf);
+ }
+
+ /*
+ * try_grow_buf_remaining() takes a uint32_t, so a bytes count that is
+ * itself a multiple of 2^32 truncates to 0 on the implicit conversion
+ * if the "bytes > MAX_BUF_SIZE" check above is ever lost -- remaining
+ * space then looks sufficient and the memcpy() below still copies the
+ * full untruncated count. Only reachable where size_t is wider than
+ * uint32_t; NULL is safe here because the guard must reject this
+ * before ever touching ptr.
+ */
+ if (sizeof(size_t) > sizeof(uint32_t)) {
+ buf_t *buf = init_buf(16);
+
+ ck_assert_int_eq(buf_append_bytes(buf, NULL,
+ (((size_t) UINT32_MAX) + 1)),
+ ESLURM_DATA_TOO_LARGE);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+ ck_assert_int_eq(size_buf(buf), 16);
+
+ free_buf(buf);
+ }
+}
+
+END_TEST
+
+START_TEST(test_buf_append_str)
+{
+ buf_t *buf = init_buf(16);
+
+ /* a NULL string is treated like an empty one, not a fatal error */
+ ck_assert_int_eq(buf_append_str(buf, NULL), SLURM_SUCCESS);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+
+ /* appending an empty string is a no-op success */
+ ck_assert_int_eq(buf_append_str(buf, ""), SLURM_SUCCESS);
+ ck_assert_int_eq(get_buf_offset(buf), 0);
+
+ ck_assert_int_eq(buf_append_str(buf, "abc"), SLURM_SUCCESS);
+ /* the NUL terminator must not be appended */
+ ck_assert_int_eq(get_buf_offset(buf), 3);
+
+ ck_assert_int_eq(buf_append_str(buf, "def"), SLURM_SUCCESS);
+ ck_assert_int_eq(get_buf_offset(buf), 6);
+ ck_assert_msg(!memcmp(get_buf_data(buf), "abcdef", 6),
+ "appended strings do not match the source");
+
+ ck_assert_int_eq(buf_append_str(buf, ""), SLURM_SUCCESS);
+ ck_assert_int_eq(get_buf_offset(buf), 6);
+
+ free_buf(buf);
+}
+
+END_TEST
+
+START_TEST(test_buf_append_str_grow)
+{
+ char *str = xmalloc(BUF_SIZE + 1);
+ buf_t *buf = init_buf(16);
+
+ memset(str, 'a', BUF_SIZE);
+
+ ck_assert_int_eq(buf_append_str(buf, str), SLURM_SUCCESS);
+ ck_assert_int_eq(get_buf_offset(buf), BUF_SIZE);
+ ck_assert_int_eq(size_buf(buf), (16 + (2 * BUF_SIZE)));
+ ck_assert_msg(!memcmp(get_buf_data(buf), str, BUF_SIZE),
+ "buf_append_str() did not preserve the contents");
+
+ free_buf(buf);
+ xfree(str);
+}
+
+END_TEST
+
+START_TEST(test_assign_buf)
+{
+ char *data = xmalloc(64);
+ buf_t *buf = init_buf(16);
+
+ memcpy(data, "assigned", 8);
+
+ assign_buf(buf, &data, 8);
+ /* assign_buf() takes ownership of data */
+ ck_assert(data == NULL);
+ /* the buffer is sized by the allocation, not by the byte count */
+ ck_assert_int_eq(size_buf(buf), 64);
+ ck_assert_int_eq(get_buf_offset(buf), 8);
+ ck_assert_int_eq(remaining_buf(buf), (64 - 8));
+ ck_assert_msg(!memcmp(get_buf_data(buf), "assigned", 8),
+ "assign_buf() did not preserve the contents");
+
+ free_buf(buf);
+}
+
+END_TEST
+
+START_TEST(test_xfer_buf_data)
+{
+ buf_t *buf = init_buf(16);
+ char *data;
+
+ ck_assert_int_eq(buf_append_str(buf, "xfer"), SLURM_SUCCESS);
+
+ data = xfer_buf_data(buf);
+ /* the buf_t is released and the caller now owns the data */
+ ck_assert(buf == NULL);
+ ck_assert(data != NULL);
+ ck_assert_msg(!memcmp(data, "xfer", 4),
+ "xfer_buf_data() did not preserve the contents");
+
+ xfree(data);
+}
+
+END_TEST
+
+static Suite *suite_buf(void)
+{
+ Suite *s = suite_create("buf");
+ TCase *tc_core = tcase_create("buf");
+
+ tcase_add_test(tc_core, test_init_buf);
+ tcase_add_test(tc_core, test_try_init_buf);
+ tcase_add_test(tc_core, test_create_buf);
+ tcase_add_test(tc_core, test_create_shadow_buf);
+ tcase_add_test(tc_core, test_create_mmap_buf);
+ tcase_add_test(tc_core, test_shadow_buf_initializer);
+ tcase_add_test(tc_core, test_buf_macros);
+ tcase_add_test(tc_core, test_grow_buf);
+ tcase_add_test(tc_core, test_try_grow_buf);
+ tcase_add_test(tc_core, test_try_grow_buf_remaining);
+ tcase_add_test(tc_core, test_buf_append_bytes);
+ tcase_add_test(tc_core, test_buf_append_bytes_grow);
+ tcase_add_test(tc_core, test_buf_append_bytes_too_large);
+ tcase_add_test(tc_core, test_buf_append_str);
+ tcase_add_test(tc_core, test_buf_append_str_grow);
+ tcase_add_test(tc_core, test_assign_buf);
+ tcase_add_test(tc_core, test_xfer_buf_data);
+
+ suite_add_tcase(s, tc_core);
+ return s;
+}
+
+int main(void)
+{
+ int number_failed;
+ log_options_t log_opts = LOG_OPTS_INITIALIZER;
+ const char *debug_env = getenv("SLURM_DEBUG");
+ const char *debug_flags_env = getenv("SLURM_DEBUG_FLAGS");
+ SRunner *sr;
+
+ if (debug_env)
+ log_opts.stderr_level = log_string2num(debug_env);
+ if (debug_flags_env)
+ debug_str2flags(debug_flags_env, &slurm_conf.debug_flags);
+
+ log_init("buf-test", log_opts, 0, NULL);
+
+ sr = srunner_create(suite_buf());
+
+ srunner_run_all(sr, CK_ENV);
+ number_failed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ log_fini();
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/testsuite/slurm_unit/common/xstring-test.c b/testsuite/slurm_unit/common/xstring-test.c
index 961800f..91ac9da 100644
--- a/testsuite/slurm_unit/common/xstring-test.c
+++ b/testsuite/slurm_unit/common/xstring-test.c
@@ -97,12 +97,64 @@
}
END_TEST
+START_TEST(test_try_xstrndup)
+{
+ char *str = NULL, *xstr = NULL;
+
+ ck_assert(try_xstrndup(NULL, 10) == NULL);
+
+ /* n larger than the string must copy the entire string */
+ str = try_xstrndup("abcdef", 10);
+ ck_assert(str != NULL);
+ ck_assert_str_eq(str, "abcdef");
+ /* the result is always NUL terminated */
+ ck_assert_int_eq(xsize(str), 7);
+ xfree(str);
+
+ /* n smaller than the string must truncate the copy */
+ str = try_xstrndup("abcdef", 3);
+ ck_assert(str != NULL);
+ ck_assert_str_eq(str, "abc");
+ ck_assert_int_eq(xsize(str), 4);
+ xfree(str);
+
+ /* copying must stop at the first NUL */
+ str = try_xstrndup("abc\0def", 7);
+ ck_assert(str != NULL);
+ ck_assert_str_eq(str, "abc");
+ ck_assert_int_eq(xsize(str), 4);
+ xfree(str);
+
+ /* an empty string must not return NULL */
+ str = try_xstrndup("", 10);
+ ck_assert(str != NULL);
+ ck_assert_str_eq(str, "");
+ xfree(str);
+
+ /* an n of zero must not return NULL */
+ str = try_xstrndup("abcdef", 0);
+ ck_assert(str != NULL);
+ ck_assert_str_eq(str, "");
+ xfree(str);
+
+ /* results must match xstrndup() */
+ str = try_xstrndup("abcdef", 3);
+ xstr = xstrndup("abcdef", 3);
+ ck_assert_str_eq(str, xstr);
+ ck_assert_int_eq(xsize(str), xsize(xstr));
+ xfree(str);
+ xfree(xstr);
+}
+
+END_TEST
+
Suite *xstring_suite(void)
{
Suite *s = suite_create("xstring");
TCase *tc_core = tcase_create("Core");
tcase_add_loop_test(tc_core, test_xstrtrim, 0 , sizeof(xstrtrim_data) /
sizeof(xstrtrim_data_t) );
+ tcase_add_test(tc_core, test_try_xstrndup);
suite_add_tcase(s, tc_core);
return s;
}