msgb: introduce extended copy functions

Those are similar to existing *msgb_alloc*() functions but allows
to change the size of destination msgb provided it fits the
data from source msgb.

Change-Id: I36d4c16241d19f0f73c325be4d0e0bdef6813615
Signed-off-by: Max <msuraev@sysmocom.de>
diff --git a/include/osmocom/core/msgb.h b/include/osmocom/core/msgb.h
index 1e2b023..3fdb189 100644
--- a/include/osmocom/core/msgb.h
+++ b/include/osmocom/core/msgb.h
@@ -70,6 +70,8 @@
 	int old_size, int new_size);
 extern struct msgb *msgb_copy(const struct msgb *msg, const char *name);
 extern struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name);
+extern struct msgb *msgb_copy_resize(const struct msgb *msg, uint16_t new_len, const char *name);
+extern struct msgb *msgb_copy_resize_c(const void *ctx, const struct msgb *msg, uint16_t new_len, const char *name);
 static int msgb_test_invariant(const struct msgb *msg) __attribute__((pure));
 
 /*! Free all msgbs from a queue built with msgb_enqueue().
diff --git a/src/msgb.c b/src/msgb.c
index 0881a55..713510c 100644
--- a/src/msgb.c
+++ b/src/msgb.c
@@ -314,31 +314,39 @@
 	return tall_msgb_ctx;
 }
 
-/*! Copy an msgb.
+/*! Copy an msgb with memory reallocation.
  *
- *  This function allocates a new msgb, copies the data buffer of msg,
- *  and adjusts the pointers (incl l1h-l4h) accordingly. The cb part
- *  is not copied.
+ *  This function allocates a new msgb with new_len size, copies the data buffer of msg,
+ *  and adjusts the pointers (incl l1h-l4h) accordingly. The cb part is not copied.
+ *  \param[in] ctx  talloc context on which allocation happens
  *  \param[in] msg  The old msgb object
- *  \param[in] name Human-readable name to be associated with msgb
+ *  \param[in] new_len The length of new msgb object
+ *  \param[in] name Human-readable name to be associated with new msgb
  */
-struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name)
+struct msgb *msgb_copy_resize_c(const void *ctx, const struct msgb *msg, uint16_t new_len, const char *name)
 {
 	struct msgb *new_msg;
 
-	new_msg = msgb_alloc_c(ctx, msg->data_len, name);
+	if (new_len < msgb_length(msg)) {
+		LOGP(DLGLOBAL, LOGL_ERROR,
+			 "Data from old msgb (%u bytes) won't fit into new msgb (%u bytes) after reallocation\n",
+			 msgb_length(msg), new_len);
+		return NULL;
+	}
+
+	new_msg = msgb_alloc_c(ctx, new_len, name);
 	if (!new_msg)
 		return NULL;
 
-	/* copy data */
-	memcpy(new_msg->_data, msg->_data, new_msg->data_len);
-
 	/* copy header */
 	new_msg->len = msg->len;
 	new_msg->data += msg->data - msg->_data;
 	new_msg->head += msg->head - msg->_data;
 	new_msg->tail += msg->tail - msg->_data;
 
+	/* copy data */
+	memcpy(new_msg->data, msg->data, msgb_length(msg));
+
 	if (msg->l1h)
 		new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
 	if (msg->l2h)
@@ -351,6 +359,32 @@
 	return new_msg;
 }
 
+/*! Copy an msgb with memory reallocation.
+ *
+ *  This function allocates a new msgb with new_len size, copies the data buffer of msg,
+ *  and adjusts the pointers (incl l1h-l4h) accordingly. The cb part is not copied.
+ *  \param[in] msg  The old msgb object
+ *  \param[in] name Human-readable name to be associated with new msgb
+ */
+struct msgb *msgb_copy_resize(const struct msgb *msg, uint16_t new_len, const char *name)
+{
+	return msgb_copy_resize_c(tall_msgb_ctx, msg, new_len, name);
+}
+
+/*! Copy an msgb.
+ *
+ *  This function allocates a new msgb, copies the data buffer of msg,
+ *  and adjusts the pointers (incl l1h-l4h) accordingly. The cb part
+ *  is not copied.
+ *  \param[in] ctx  talloc context on which allocation happens
+ *  \param[in] msg  The old msgb object
+ *  \param[in] name Human-readable name to be associated with msgb
+ */
+struct msgb *msgb_copy_c(const void *ctx, const struct msgb *msg, const char *name)
+{
+	return msgb_copy_resize_c(ctx, msg, msg->data_len, name);
+}
+
 /*! Copy an msgb.
  *
  *  This function allocates a new msgb, copies the data buffer of msg,