Initial commit.
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 8 Jan 2016 17:09:33 +0000 (17:09 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Fri, 8 Jan 2016 17:09:33 +0000 (17:09 +0000)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
ilenstr.h [new file with mode: 0644]
test.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..64d19ef
--- /dev/null
@@ -0,0 +1,3 @@
+test
+*~
+*.o
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..bb208ee
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+CC     = gcc
+CFLAGS = -O2 -Wall -g
+
+all: test
+
+test: test.o
+       $(CC) $(CFLAGS) $< -o $@
+
+clean:
+       rm -f *~ test *.o
diff --git a/ilenstr.h b/ilenstr.h
new file mode 100644 (file)
index 0000000..041fdb4
--- /dev/null
+++ b/ilenstr.h
@@ -0,0 +1,86 @@
+/* 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_ */
diff --git a/test.c b/test.c
new file mode 100644 (file)
index 0000000..cffc810
--- /dev/null
+++ b/test.c
@@ -0,0 +1,26 @@
+#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);
+}