void *tal_memdup(const tal_t *ctx, const void *p, size_t n)
{
- void *ret = tal_arr(ctx, char, n);
+ void *ret;
+
+ if (ctx == TAL_TAKE)
+ return (void *)p;
+
+ ret = tal_arr(ctx, char, n);
if (ret)
memcpy(ret, p, n);
return ret;
char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap)
{
size_t max = strlen(fmt) * 2;
- char *buf = tal_arr(ctx, char, max);
+ char *buf;
int ret;
+ if (ctx == TAL_TAKE)
+ buf = tal_arr(tal_parent(fmt), char, max);
+ else
+ buf = tal_arr(ctx, char, max);
+
while (buf) {
va_list ap2;
break;
buf = tal_resize(buf, max *= 2);
}
+ if (ctx == TAL_TAKE)
+ tal_free(fmt);
return buf;
}
*/
typedef void tal_t;
+/**
+ * TAL_TAKE - fake tal_t to indicate function will own arguments.
+ *
+ * Various functions take a context on which to allocate: if you use
+ * TAL_TAKE there instead, it means that the argument(s) are actually
+ * tal objects. The returned value will share the same parent; it may
+ * even be the same pointer as the arguments. The arguments themselves
+ * will be reused, freed, or made a child of the return value: they are
+ * no longer valid for external use.
+ */
+#define TAL_TAKE ((tal_t *)-2L)
+
/**
* tal - basic allocator function
* @ctx: NULL, or tal allocated object to be parent.
/**
* tal_memdup - duplicate memory.
- * @ctx: NULL, or tal allocated object to be parent.
+ * @ctx: NULL, or tal allocated object to be parent (or TAL_TAKE).
* @p: the memory to copy
* @n: the number of bytes.
+ *
*/
void *tal_memdup(const tal_t *ctx, const void *p, size_t n);
/**
- * tal_strdup - duplicate a string.
- * @ctx: NULL, or tal allocated object to be parent.
+ * tal_strdup - duplicate a string
+ * @ctx: NULL, or tal allocated object to be parent (or TAL_TAKE).
* @p: the string to copy
*/
char *tal_strdup(const tal_t *ctx, const char *p);
/**
* tal_strndup - duplicate a limited amount of a string.
- * @ctx: NULL, or tal allocated object to be parent.
+ * @ctx: NULL, or tal allocated object to be parent (or TAL_TAKE).
* @p: the string to copy
* @n: the maximum length to copy.
*
/**
* tal_asprintf - allocate a formatted string
- * @ctx: NULL, or tal allocated object to be parent.
+ * @ctx: NULL, or tal allocated object to be parent (or TAL_TAKE).
* @fmt: the printf-style format.
+ *
+ * If @ctx is TAL_TAKE, @fmt is freed and its parent will be the parent
+ * of the return value.
*/
char *tal_asprintf(const tal_t *ctx, const char *fmt, ...) PRINTF_FMT(2,3);
/**
* tal_vasprintf - allocate a formatted string (va_list version)
- * @ctx: NULL, or tal allocated object to be parent.
+ * @ctx: NULL, or tal allocated object to be parent (or TAL_TAKE).
* @fmt: the printf-style format.
* @va: the va_list containing the format args.
+ *
+ * If @ctx is TAL_TAKE, @fmt is freed and its parent will be the parent
+ * of the return value.
*/
char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap)
PRINTF_FMT(2,0);
--- /dev/null
+#include <ccan/tal/tal.h>
+#include <ccan/tal/tal.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ char *parent, *c;
+
+ plan_tests(13);
+
+ parent = tal(NULL, char);
+ ok1(parent);
+
+ c = tal_strdup(parent, "hello");
+
+ c = tal_strdup(TAL_TAKE, c);
+ ok1(strcmp(c, "hello") == 0);
+ ok1(tal_parent(c) == parent);
+
+ c = tal_strndup(TAL_TAKE, c, 5);
+ ok1(strcmp(c, "hello") == 0);
+ ok1(tal_parent(c) == parent);
+
+ c = tal_strndup(TAL_TAKE, c, 3);
+ ok1(strcmp(c, "hel") == 0);
+ ok1(tal_parent(c) == parent);
+
+ c = tal_memdup(TAL_TAKE, c, 1);
+ ok1(c[0] == 'h');
+ ok1(tal_parent(c) == parent);
+
+ /* No leftover allocations. */
+ tal_free(c);
+ ok1(tal_first(parent) == NULL);
+
+ c = tal_strdup(parent, "hello %s");
+ c = tal_asprintf(TAL_TAKE, c, "there");
+ ok1(strcmp(c, "hello there") == 0);
+ ok1(tal_parent(c) == parent);
+ /* No leftover allocations. */
+ tal_free(c);
+ ok1(tal_first(parent) == NULL);
+
+ tal_free(parent);
+
+ return exit_status();
+}