From: Richard W.M. Jones Date: Fri, 8 Jan 2016 17:09:33 +0000 (+0000) Subject: Initial commit. X-Git-Url: http://git.annexia.org/?a=commitdiff_plain;h=fcab2dff7f69855df1df7b984a92a4359ebb1028;p=ilenstr.git Initial commit. --- fcab2dff7f69855df1df7b984a92a4359ebb1028 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64d19ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +test +*~ +*.o diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..041fdb4 --- /dev/null +++ b/ilenstr.h @@ -0,0 +1,86 @@ +/* Strings with implicit length. + * Richard W.M. Jones + * + * 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 +#include +#include +#include + +/* 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 index 0000000..cffc810 --- /dev/null +++ b/test.c @@ -0,0 +1,26 @@ +#include +#include +#include +#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); +}