--- /dev/null
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+ /** A generic data buffer structure for encoding and decoding
+ *
+ * Because doing manual length checks is error prone and a waste of everyone's time.
+ *
+ * @file src/lib/util/dbuff.c
+ *
+ * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+
+RCSID("$Id:")
+
+#include <freeradius-devel/util/dbuff.h>
+
+#if defined(__clang_analyzer__) || !defined(NDEBUG)
+# define CHECK_DBUFF_INIT(_sbuff) if (!(_sbuff)->extend && (unlikely(!(_sbuff)->buff) || unlikely(!(_sbuff)->start) || unlikely(!(_sbuff)->end) || unlikely(!(_sbuff)->p))) return 0;
+#else
+# define CHECK_DBUFF_INIT(_sbuff)
+#endif
+
+/** start/end flavored mem{cpy,move} wrapper with sanity checks
+ */
+static inline CC_HINT(always_inline) ssize_t safecpy(uint8_t *o_start, uint8_t *o_end,
+ uint8_t const *i_start, uint8_t const *i_end)
+{
+ ssize_t diff;
+ size_t i_len = i_end - i_start;
+
+ if (unlikely((o_end < o_start) || (i_end < i_start))) return 0; /* sanity check */
+
+ diff = (o_end - o_start) - (i_len);
+ if (diff < 0) return diff;
+
+ if ((i_start > o_end) || (i_end < o_start)) { /* no-overlap */
+ memcpy(o_start, i_start, i_len);
+ } else { /* overlap */
+ memmove(o_start, i_start, i_len);
+ }
+
+ return (i_len);
+}
+
+/** Move data from one dbuff to another
+ *
+ * @note Do not call this function directly; use #fr_dbuff_move
+ *
+ * Both in and out will be advanced by
+ * min {len, fr_dbuff_remaining(out), fr_dbuff_remaining(in)}; eventually,
+ * we'll attempt to extend dbuffs where possible and needed to make len bytes
+ * available in both in and out.
+ *
+ * @param[in] out dbuff to copy data to.
+ * @param[in] in dbuff to copy data from.
+ * @param[in] len Maximum number of bytes to copy.
+ * @return The amount of data copied.
+ */
+size_t _fr_dbuff_move_dbuff_to_dbuff(fr_dbuff_t *out, fr_dbuff_t *in, size_t len)
+{
+ size_t o_remaining = fr_dbuff_remaining(out);
+ size_t i_remaining = fr_dbuff_remaining(in);
+ size_t to_copy = len;
+ if (to_copy > o_remaining) to_copy = o_remaining;
+ if (to_copy > i_remaining) to_copy = i_remaining;
+ safecpy(fr_dbuff_current(out), fr_dbuff_end(out), fr_dbuff_current(in), fr_dbuff_current(in) + to_copy);
+ return fr_dbuff_advance(out, fr_dbuff_advance(in, to_copy));
+}
+
+/** Move data from a marker to a dbuff
+ *
+ * @note Do not call this function directly; use #fr_dbuff_move
+ *
+ * Both in and out will be advanced by
+ * min {len, fr_dbuff_remaining(out), fr_dbuff_marker_remaining(in)}; eventually,
+ * we'll attempt to extend dbuffs where possible and needed to make len bytes
+ * available in both in and out.
+ *
+ * @param[in] out dbuff to copy data to.
+ * @param[in] in marker to copy data from.
+ * @param[in] len Maximum number of bytes to copy.
+ * @return The amount of data copied.
+ */
+size_t _fr_dbuff_move_marker_to_dbuff(fr_dbuff_t *out, fr_dbuff_marker_t *in, size_t len)
+{
+ size_t o_remaining = fr_dbuff_remaining(out);
+ size_t i_remaining = fr_dbuff_marker_remaining(in);
+ size_t to_copy = len;
+ if (to_copy > o_remaining) to_copy = o_remaining;
+ if (to_copy > i_remaining) to_copy = i_remaining;
+ safecpy(fr_dbuff_current(out), fr_dbuff_end(out), fr_dbuff_marker_current(in),
+ fr_dbuff_marker_current(in) + to_copy);
+ return fr_dbuff_advance(out, fr_dbuff_marker_advance(in, to_copy));
+}
+
+/** Move data from one marker to another
+ *
+ * @note Do not call this function directly; use #fr_dbuff_move
+ *
+ * Both in and out will be advanced by
+ * min {len, fr_dbuff_marker_remaining(out), fr_dbuff_marker_remaining(in)}; eventually,
+ * we'll attempt to extend dbuffs where possible and needed to make len bytes
+ * available in both in and out.
+ *
+ * @param[in] out dbuff to copy data to.
+ * @param[in] in marker to copy data from.
+ * @param[in] len Maximum number of bytes to copy.
+ * @return The amount of data copied.
+ */
+size_t _fr_dbuff_move_marker_to_marker(fr_dbuff_marker_t *out, fr_dbuff_marker_t *in, size_t len)
+{
+ size_t o_remaining = fr_dbuff_marker_remaining(out);
+ size_t i_remaining = fr_dbuff_marker_remaining(in);
+ size_t to_copy = len;
+ if (to_copy > o_remaining) to_copy = o_remaining;
+ if (to_copy > i_remaining) to_copy = i_remaining;
+ safecpy(fr_dbuff_marker_current(out), fr_dbuff_marker_end(out), fr_dbuff_marker_current(in),
+ fr_dbuff_marker_current(in) + to_copy);
+ return fr_dbuff_marker_advance(out, fr_dbuff_marker_advance(in, to_copy));
+}
+
+/** Move data from a dbuff to a marker
+ *
+ * @note Do not call this function directly; use #fr_dbuff_move
+ *
+ * Both in and out will be advanced by
+ * min {len, fr_dbuff_marker_remaining(out), fr_dbuff_marker_remaining(in)}; eventually,
+ * we'll attempt to extend dbuffs where possible and needed to make len bytes
+ * available in both in and out.
+ *
+ * @param[in] out dbuff to copy data to.
+ * @param[in] in marker to copy data from.
+ * @param[in] len Maximum number of bytes to copy.
+ * @return The amount of data copied.
+ */
+size_t _fr_dbuff_move_dbuff_to_marker(fr_dbuff_marker_t *out, fr_dbuff_t *in, size_t len)
+{
+ size_t o_remaining = fr_dbuff_marker_remaining(out);
+ size_t i_remaining = fr_dbuff_remaining(in);
+ size_t to_copy = len;
+ if (to_copy > o_remaining) to_copy = o_remaining;
+ if (to_copy > i_remaining) to_copy = i_remaining;
+ safecpy(fr_dbuff_marker_current(out), fr_dbuff_marker_end(out), fr_dbuff_current(in),
+ fr_dbuff_current(in) + to_copy);
+ return fr_dbuff_marker_advance(out, fr_dbuff_advance(in, to_copy));
+}
{
return m->p - m->parent->start;
}
+
+/* what is the end of the buffer (determined from marker)
+ *
+ */
+static inline uint8_t *fr_dbuff_marker_end(fr_dbuff_marker_t *m)
+{
+ return m->parent->end;
+}
/** @} */
/** @name copy data to dbuff
return fr_dbuff_memcpy_in(dbuff, ((uint8_t *)&num) + (sizeof(uint64_t) - ret), ret);
}
+
+size_t _fr_dbuff_move_dbuff_to_dbuff(fr_dbuff_t *out, fr_dbuff_t *in, size_t len);
+
+size_t _fr_dbuff_move_marker_to_dbuff(fr_dbuff_t *out, fr_dbuff_marker_t *in, size_t len);
+
+size_t _fr_dbuff_move_marker_to_marker(fr_dbuff_marker_t *out, fr_dbuff_marker_t *in, size_t len);
+
+size_t _fr_dbuff_move_dbuff_to_marker(fr_dbuff_marker_t *out, fr_dbuff_t *in, size_t len);
+
+/** Copy in as many bytes as possible from one dbuff or marker to another
+ *
+ * @param[in] out to copy into.
+ * @param[in] in to copy from.
+ * @param[in] len The maximum length to copy.
+ * @return Number of bytes to copy.
+ */
+#define fr_dbuff_move(_out, _in, _len) \
+ _Generic((_out), \
+ fr_dbuff_t * : \
+ _Generic((_in), \
+ fr_dbuff_t * : _fr_dbuff_move_dbuff_to_dbuff((fr_dbuff_t *)_out, \
+ (fr_dbuff_t *)_in, _len), \
+ fr_dbuff_marker_t * : _fr_dbuff_move_marker_to_dbuff((fr_dbuff_t *)_out, \
+ (fr_dbuff_marker_t *)_in, _len) \
+ ), \
+ fr_dbuff_marker_t * : \
+ _Generic((_in), \
+ fr_dbuff_t * : _fr_dbuff_move_dbuff_to_marker((fr_dbuff_marker_t *)_out, \
+ (fr_dbuff_t *)_in, _len), \
+ fr_dbuff_marker_t * : _fr_dbuff_move_marker_to_marker((fr_dbuff_marker_t *)_out, \
+ (fr_dbuff_marker_t *)_in, _len) \
+ ) \
+ )
/** @} */
#ifdef __cplusplus
TEST_CHECK(fr_dbuff_in(&dbuff, u64val) == -(ssize_t)(sizeof(uint64_t) - sizeof(uint32_t)));
}
+static void test_dbuff_move(void)
+{
+ uint8_t buff1[26], buff2[26], buff3[10];
+ fr_dbuff_t dbuff1, dbuff2, dbuff3;
+ fr_dbuff_marker_t marker1, marker2;
+
+ memcpy(buff1, "abcdefghijklmnopqrstuvwxyz", sizeof(buff1));
+ memcpy(buff2, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", sizeof(buff2));
+ memcpy(buff3, "0123456789", sizeof(buff3));
+ fr_dbuff_init(&dbuff1, buff1, sizeof(buff1));
+ fr_dbuff_init(&dbuff2, buff2, sizeof(buff2));
+ fr_dbuff_init(&dbuff3, buff3, sizeof(buff3));
+ fr_dbuff_marker(&marker1, &dbuff1);
+ fr_dbuff_marker(&marker2, &dbuff2);
+
+ TEST_CASE("move dbuff to dbuff");
+ TEST_CHECK(fr_dbuff_move(&dbuff1, &dbuff2, 13) == 13);
+ TEST_CHECK(fr_dbuff_used(&dbuff1) == 13);
+ TEST_CHECK(fr_dbuff_used(&dbuff2) == 13);
+ TEST_CHECK(memcmp(dbuff1.start, "ABCDEFGHIJKLMnopqrstuvwxyz", 26) == 0);
+
+ TEST_CASE("move dbuff to marker");
+ fr_dbuff_marker_advance(&marker2, 4);
+ TEST_CHECK(fr_dbuff_move(&marker2, &dbuff3, 10) == 10);
+ TEST_CHECK(fr_dbuff_marker_used(&marker2) == 14);
+ TEST_CHECK(memcmp(dbuff2.start, "ABCD0123456789OPQRSTUVWXYZ", 26) == 0);
+
+ TEST_CASE("move marker to dbuff");
+ fr_dbuff_marker_advance(&marker1, 7);
+ TEST_CHECK(fr_dbuff_move(&dbuff1, &marker1, 6) == 6);
+ TEST_CHECK(fr_dbuff_used(&dbuff1) == 19);
+ TEST_CHECK(fr_dbuff_marker_used(&marker1) == 13);
+ TEST_CHECK(memcmp(dbuff1.start, "ABCDEFGHIJKLMHIJKLMtuvwxyz", 26) == 0);
+
+ TEST_CASE("move marker to marker");
+ TEST_CHECK(fr_dbuff_move(&marker2, &marker1, 8) == 8);
+ TEST_CHECK(fr_dbuff_marker_used(&marker1) == 21);
+ TEST_CHECK(fr_dbuff_marker_used(&marker2) == 22);
+ TEST_CHECK(memcmp(dbuff2.start, "ABCD0123456789HIJKLMtuWXYZ", 26) == 0);
+}
+
TEST_LIST = {
/*
{ "fr_dbuff_init_no_parent", test_dbuff_init_no_parent },
{ "fr_dbuff_max", test_dbuff_max },
{ "fr_dbuff_in", test_dbuff_net_encode },
+ { "fr_dbuff_move", test_dbuff_move },
{ NULL }
};