Fix URL to point to blog posting.
[ilenstr.git] / ilenstr.h
1 /* Strings with implicit length.
2  * Richard W.M. Jones <rjones@redhat.com>
3  *
4  * https://rwmj.wordpress.com/2016/01/08/half-baked-ideas-c-strings-with-implicit-length-field/
5  *
6  * Inspired by this OCaml implementation of strings:
7  * https://rwmj.wordpress.com/2009/08/05/ocaml-internals-part-2-strings-and-other-types/
8  *
9  * NB: It's just for playing with.  Don't actually use this in
10  * production code!!  In particular this uses malloc allocations in a
11  * way which is not valid, standard C, and may break or corrupt things
12  * at any time.
13  */
14
15 #ifndef ilenstr_h_
16 #define ilenstr_h_
17
18 #include <stdint.h>
19 #include <string.h>
20 #include <malloc.h>
21 #include <assert.h>
22
23 /* If your libc doesn't have glibc's malloc_usable_size, then
24  * you need to find the equivalent function and define it here.
25  */
26 #define ilenstr_malloc_size malloc_usable_size
27
28 /* Although ilenstrs have a separate length field, it's implicit,
29  * so they are convertable back to C strings (although C strings
30  * are NOT ilenstrs so don't try to cast them the other way).
31  */
32 typedef char *ilenstr;
33
34 /* Allocate a new ilenstr from an arbitrary string + length.  The
35  * string may contain \0 characters within the string.
36  */
37 static ilenstr
38 new_ilenstr_with_len (const char *str, size_t len)
39 {
40   char *ilstr;
41   size_t msize;
42   uint8_t *p;
43
44   ilstr = malloc (len+1);
45   if (!ilstr) return NULL;
46   memcpy (ilstr, str, len);
47   ilstr[len] = '\0';
48
49   msize = ilenstr_malloc_size (ilstr);
50   assert (len <= msize);
51
52   /* Because we only have a single byte to use at the end of the
53    * malloc allocation, we have this limit.  It could be bypassed with
54    * some horrible encoding scheme, but that would make getting the
55    * string length slower.  Hopefully this never asserts in practice.
56    */
57   assert (msize-len <= 255);
58
59   p = (uint8_t *) &ilstr[msize-1];
60   *p = msize-len;
61
62   return ilstr;
63 }
64
65 /* Allocate an ilenstr from a C string.  The C string cannot
66  * contain \0 characters within the string.
67  */
68 static ilenstr
69 new_ilenstr (const char *cstr)
70 {
71   return new_ilenstr_with_len (cstr, strlen (cstr));
72 }
73
74 /* Get the implicit length of an ilenstr. */
75 static size_t
76 ilstrlen (const ilenstr ilstr)
77 {
78   size_t msize;
79   uint8_t *p;
80
81   msize = ilenstr_malloc_size (ilstr);
82   p = (uint8_t *) (ilstr + msize - 1);
83   return msize - *p;
84 }
85
86 #endif /* ilenstr_h_ */