--- /dev/null
+/* Strings with implicit length.
+ * Richard W.M. Jones <rjones@redhat.com>
+ *
+ * https://rwmj.wordpress.com
+ *
+ * Inspired by this OCaml implementation of strings:
+ * https://rwmj.wordpress.com/2009/08/05/ocaml-internals-part-2-strings-and-other-types/
+ *
+ * NB: It's just for playing with. Don't actually use this in
+ * production code!! In particular this uses malloc allocations in a
+ * way which is not valid, standard C, and may break or corrupt things
+ * at any time.
+ */
+
+#ifndef ilenstr_h_
+#define ilenstr_h_
+
+#include <stdint.h>
+#include <string.h>
+#include <malloc.h>
+#include <assert.h>
+
+/* If your libc doesn't have glibc's malloc_usable_size, then
+ * you need to find the equivalent function and define it here.
+ */
+#define ilenstr_malloc_size malloc_usable_size
+
+/* Although ilenstrs have a separate length field, it's implicit,
+ * so they are convertable back to C strings (although C strings
+ * are NOT ilenstrs so don't try to cast them the other way).
+ */
+typedef char *ilenstr;
+
+/* Allocate a new ilenstr from an arbitrary string + length. The
+ * string may contain \0 characters within the string.
+ */
+static ilenstr
+new_ilenstr_with_len (const char *str, size_t len)
+{
+ char *ilstr;
+ size_t msize;
+ uint8_t *p;
+
+ ilstr = malloc (len+1);
+ if (!ilstr) return NULL;
+ memcpy (ilstr, str, len);
+ ilstr[len] = '\0';
+
+ msize = ilenstr_malloc_size (ilstr);
+ assert (len <= msize);
+
+ /* Because we only have a single byte to use at the end of the
+ * malloc allocation, we have this limit. It could be bypassed with
+ * some horrible encoding scheme, but that would make getting the
+ * string length slower. Hopefully this never asserts in practice.
+ */
+ assert (msize-len <= 255);
+
+ p = (uint8_t *) &ilstr[msize-1];
+ *p = msize-len;
+
+ return ilstr;
+}
+
+/* Allocate an ilenstr from a C string. The C string cannot
+ * contain \0 characters within the string.
+ */
+static ilenstr
+new_ilenstr (const char *cstr)
+{
+ return new_ilenstr_with_len (cstr, strlen (cstr));
+}
+
+/* Get the implicit length of an ilenstr. */
+static size_t
+ilstrlen (const ilenstr ilstr)
+{
+ size_t msize;
+ uint8_t *p;
+
+ msize = ilenstr_malloc_size (ilstr);
+ p = (uint8_t *) (ilstr + msize - 1);
+ return msize - *p;
+}
+
+#endif /* ilenstr_h_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ilenstr.h"
+
+int
+main ()
+{
+ ilenstr ilstr;
+
+ /* C-compatible string. */
+ ilstr = new_ilenstr ("hello");
+ printf ("ilstr = %s\n", ilstr);
+ printf ("len(ilstr) = %zu\n", ilstrlen (ilstr));
+ free (ilstr);
+
+ /* String containing a \0 character. */
+ ilstr = new_ilenstr_with_len ("hello\0world", 11);
+ /* Passing the string to a legacy function only prints
+ * up to the \0 character.
+ */
+ printf ("ilstr = %s // should be truncated\n", ilstr);
+ /* The length function still works. */
+ printf ("len(ilstr) = %zu\n", ilstrlen (ilstr));
+ free (ilstr);
+}