changeset 359:7544e9990bc1

str: implement struct str in terms of struct val This changes in this commit can be summarized as: 1. it changes how VT_STR is implemented (instead of a struct str pointer, the struct val now contains the C string itself) 2. it redefines struct str to be a wrapper around struct val (in essence making struct str an alias for struct val, but preventing the compiler from silently changing between them) 3. it adds struct sym as a wrapper around struct val (similarly to struct str, this way symbol values can have distinct type and can be statically checked) This also means that some of the functions and macros around struct str changed, and new functions and macros were added to facilitate casting between struct str, struct sym, and struct val pointers. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Tue, 15 Aug 2017 22:38:16 +0300
parents 2c746904eba3
children d3db42f6c3d5
files include/jeffpc/buffer.h include/jeffpc/sexpr.h include/jeffpc/str.h include/jeffpc/urldecode.h include/jeffpc/val.h jeffpc.mapfile-vers sexpr.c sexpr.y sexpr_eval.c str.c tests/test_sexpr_eval.c tests/test_sexpr_parser.c val.c
diffstat 13 files changed, 350 insertions(+), 325 deletions(-) [+]
line wrap: on
line diff
--- a/include/jeffpc/buffer.h	Tue Aug 15 22:55:36 2017 +0300
+++ b/include/jeffpc/buffer.h	Tue Aug 15 22:38:16 2017 +0300
@@ -28,7 +28,7 @@
 #include <fcntl.h>
 
 #include <jeffpc/error.h>
-#include <jeffpc/str.h>
+#include <jeffpc/val.h>
 
 struct buffer;
 
--- a/include/jeffpc/sexpr.h	Tue Aug 15 22:55:36 2017 +0300
+++ b/include/jeffpc/sexpr.h	Tue Aug 15 22:38:16 2017 +0300
@@ -26,11 +26,10 @@
 #include <stdbool.h>
 
 #include <jeffpc/val.h>
-#include <jeffpc/str.h>
 
 struct sexpr_eval_env {
-	struct val *(*symlookup)(struct str *, struct sexpr_eval_env *);
-	void *(*fxnlookup)(struct str *, struct sexpr_eval_env *);
+	struct val *(*symlookup)(struct sym *, struct sexpr_eval_env *);
+	void *(*fxnlookup)(struct sym *, struct sexpr_eval_env *);
 	void *private;
 };
 
--- a/include/jeffpc/str.h	Tue Aug 15 22:55:36 2017 +0300
+++ b/include/jeffpc/str.h	Tue Aug 15 22:38:16 2017 +0300
@@ -23,137 +23,8 @@
 #ifndef __JEFFPC_STR_H
 #define __JEFFPC_STR_H
 
-#include <string.h>
-#include <stdbool.h>
-
-#include <jeffpc/refcnt.h>
-
-/* ref-counted string */
-
-/*
- * Changing this value by...
- *    4 will maintain the amount of padding on 32-bit systems.
- *    8 will maintain the amount of padding on both 32-bit and 64-bit systems.
- */
-#define STR_INLINE_LEN	15
-
-struct str {
-	refcnt_t refcnt;
-
-	bool static_struct:1;	/* struct str is static */
-	bool static_alloc:1;	/* char * is static */
-	bool inline_alloc:1;	/* char * is inline */
-	bool have_len:1;	/* length member is valid */
-
-	/*
-	 * Keep track of a 24-bit length of the string.  While we could use
-	 * a size_t to represent all possible lengths, we use only 24 bits
-	 * to represents strings from 0 to ~16MB in size.  This covers the
-	 * vast majority of them.  Strings that have length less than or
-	 * equal to 16777215 (0xffffff) bytes save the length in the len
-	 * member and set have_len to true.  Strings that are longer set
-	 * have_len to false and ignore the len member.
-	 */
-	uint8_t len[3];
-
-	union {
-		const char *str;
-		char inline_str[STR_INLINE_LEN + 1];
-	};
-};
-
-#define STR_STATIC_CHAR_INITIALIZER(val)		\
-		{					\
-			.inline_str = { (val), '\0' },	\
-			.len = { 0, 0, 1 },		\
-			.have_len = true,		\
-			.static_struct = true,		\
-			.static_alloc = true,		\
-			.inline_alloc = true,		\
-		}
-
-#define _STR_STATIC_INITIALIZER(val, l)			\
-		{					\
-			.str = (val),			\
-			.len = {			\
-				((l) >> 16) & 0xff,	\
-				((l) >> 8) & 0xff,	\
-				(l) & 0xff,		\
-			},				\
-			.have_len = ((l) <= 0xffffff),	\
-			.static_struct = true,		\
-			.static_alloc = true,		\
-		}
-
-#define STR_STATIC_INITIALIZER(val)			\
-		_STR_STATIC_INITIALIZER((val),		\
-					__builtin_constant_p(val) ? strlen(val) : ~0ul)
+/* The whole str/sym API is defined in val.h */
 
-/* evaluates to a struct str *, so it can be used as a value */
-#define STATIC_STR(s)					\
-	({						\
-		static struct str _s = STR_STATIC_INITIALIZER(s); \
-		&_s;					\
-	})
-
-extern struct str *str_alloc(char *s);
-extern struct str *str_alloc_static(const char *s);
-extern size_t str_len(const struct str *str);
-extern int str_cmp(const struct str *a, const struct str *b);
-extern struct str *str_dup(const char *s);
-extern struct str *str_dup_len(const char *s, size_t len);
-extern struct str *str_cat(size_t n, ...);
-extern struct str *str_printf(const char *fmt, ...)
-	__attribute__((format (printf, 1, 2)));
-extern struct str *str_vprintf(const char *fmt, va_list args);
-extern void str_free(struct str *str);
-
-extern struct str *str_empty_string(void);
-
-static inline bool str_isstatic(struct str *x)
-{
-	return x->static_struct;
-}
-
-REFCNT_INLINE_FXNS(struct str, str, refcnt, str_free, str_isstatic)
-
-#define STR_ALLOC(s)			\
-	({				\
-		struct str *_s;		\
-		_s = str_alloc(s);	\
-		ASSERT(_s);		\
-		_s;			\
-	})
-
-#define STR_ALLOC_STATIC(s)		\
-	({				\
-		struct str *_s;		\
-		_s = str_alloc_static(s);\
-		ASSERT(_s);		\
-		_s;			\
-	})
-
-#define STR_DUP(s)			\
-	({				\
-		struct str *_s;		\
-		_s = str_dup(s);	\
-		ASSERT(_s);		\
-		_s;			\
-	})
-
-#define STR_DUP_LEN(s, l)		\
-	({				\
-		struct str *_s;		\
-		_s = str_dup_len((s), (l));\
-		ASSERT(_s);		\
-		_s;			\
-	})
-
-static inline const char *str_cstr(const struct str *str)
-{
-	if (!str)
-		return NULL;
-	return str->inline_alloc ? str->inline_str : str->str;
-}
+#include <jeffpc/val.h>
 
 #endif
--- a/include/jeffpc/urldecode.h	Tue Aug 15 22:55:36 2017 +0300
+++ b/include/jeffpc/urldecode.h	Tue Aug 15 22:38:16 2017 +0300
@@ -23,7 +23,7 @@
 #ifndef __JEFFPC_URLDECODE_H
 #define __JEFFPC_URLDECODE_H
 
-#include <jeffpc/str.h>
+#include <jeffpc/val.h>
 
 extern ssize_t urldecode(const char *in, size_t len, char *out);
 extern struct str *urldecode_str(const char *in, size_t len);
--- a/include/jeffpc/val.h	Tue Aug 15 22:55:36 2017 +0300
+++ b/include/jeffpc/val.h	Tue Aug 15 22:38:16 2017 +0300
@@ -27,9 +27,9 @@
 #include <stdbool.h>
 
 #include <jeffpc/int.h>
-#include <jeffpc/str.h>
 #include <jeffpc/refcnt.h>
 #include <jeffpc/error.h>
+#include <jeffpc/types.h>
 
 /*
  * A typed value structure.
@@ -51,14 +51,19 @@
 	VT_CHAR,	/* a single (unicode) character */
 };
 
+#define STR_INLINE_LEN	15
+
 struct val {
 	enum val_type type;
 	refcnt_t refcnt;
 	bool static_struct:1;	/* struct statically allocated */
+	bool static_alloc:1;	/* pointer is static */
+	bool inline_alloc:1;	/* data is inline */
 	union {
 		const uint64_t i;
 		const bool b;
-		struct str * const str;
+		const char *str_ptr;
+		const char str_inline[STR_INLINE_LEN + 1];
 		const struct {
 			struct val *head;
 			struct val *tail;
@@ -77,7 +82,8 @@
 		 */
 		uint64_t _set_i;
 		bool _set_b;
-		struct str *_set_str;
+		const char *_set_str_ptr;
+		char _set_str_inline[STR_INLINE_LEN + 1];
 		struct {
 			struct val *head;
 			struct val *tail;
@@ -85,15 +91,78 @@
 	};
 };
 
+/* ref-counted string */
+struct str {
+	struct val val;
+};
+
+/* ref-counted symbol name */
+struct sym {
+	struct val val;
+};
+
+/*
+ * Allocation functions
+ */
+
 extern struct val *val_alloc_bool(bool v);
 extern struct val *val_alloc_char(uint64_t v);
 extern struct val *val_alloc_int(uint64_t v);
 extern struct val *val_alloc_null(void);
-/* val_alloc_{str,sym,cons} always consume the passed in references */
-extern struct val *val_alloc_str(struct str *v);
-extern struct val *val_alloc_sym(struct str *v);
+/* val_alloc_cons always consume the passed in references */
 extern struct val *val_alloc_cons(struct val *head, struct val *tail);
+
+/*
+ * Passed in string must be freed by the str code.  (I.e., we're adopting
+ * it.)
+ */
+#define str_alloc(s)		((struct str *) _strsym_alloc((s), VT_STR))
+#define sym_alloc(s)		((struct sym *) _strsym_alloc((s), VT_SYM))
+#define val_alloc_str(s)	_strsym_alloc((s), VT_STR)
+#define val_alloc_sym(s)	_strsym_alloc((s), VT_SYM)
+extern struct val *_strsym_alloc(char *s, enum val_type type);
+/*
+ * Passed in str cannot be freed, and it doesn't have to be dup'd.  (E.g.,
+ * it could be a string in .rodata.)
+ */
+#define str_alloc_static(s)	((struct str *) _strsym_alloc_static((s), VT_STR))
+#define sym_alloc_static(s)	((struct sym *) _strsym_alloc_static((s), VT_SYM))
+#define val_alloc_str_static(s)	_strsym_alloc_static((s), VT_STR)
+#define val_alloc_sym_static(s)	_strsym_alloc_static((s), VT_SYM)
+extern struct val *_strsym_alloc_static(const char *s, enum val_type type);
+/*
+ * Passed in string cannot be freed, and it must be dup'd.  (E.g., it could
+ * be a string on the stack.)
+ */
+#define str_dup(s)		((struct str *) _strsym_dup((s), VT_STR))
+#define sym_dup(s)		((struct sym *) _strsym_dup((s), VT_SYM))
+#define val_dup_str(s)		_strsym_dup((s), VT_STR)
+#define val_dup_sym(s)		_strsym_dup((s), VT_SYM)
+extern struct val *_strsym_dup(const char *s, enum val_type type);
+/*
+ * Like above, but instead of a nul-terminated string use the passed in
+ * length.
+ */
+#define str_dup_len(s, l)	((struct str *) _strsym_dup_len((s), (l), VT_STR))
+#define sym_dup_len(s, l)	((struct sym *) _strsym_dup_len((s), (l), VT_SYM))
+#define val_dup_str_len(s, l)	_strsym_dup_len((s), (l), VT_STR)
+#define val_dup_sym_len(s, l)	_strsym_dup_len((s), (l), VT_SYM)
+extern struct val *_strsym_dup_len(const char *s, size_t len,
+				   enum val_type type);
+
+/* assorted preallocated strings */
+extern struct str *str_empty_string(void);
+
+/*
+ * Free functions
+ */
+
 extern void val_free(struct val *v);
+
+/*
+ * Dumping functions
+ */
+
 extern void val_dump_file(FILE *out, struct val *v, int indent);
 
 static inline void val_dump(struct val *v, int indent)
@@ -101,63 +170,175 @@
 	val_dump_file(stderr, v, indent);
 }
 
+/*
+ * Misc functions
+ */
+
+#define str_len(s)	_strsym_len(&(s)->val)
+#define sym_len(s)	_strsym_len(&(s)->val)
+extern size_t _strsym_len(const struct val *val);
+
+#define str_cmp(a, b)	_strsym_cmp(&(a)->val, &(b)->val)
+#define sym_cmp(a, b)	_strsym_cmp(&(a)->val, &(b)->val)
+extern int _strsym_cmp(const struct val *a, const struct val *b);
+
+extern struct str *str_cat(size_t n, ...);
+extern struct str *str_printf(const char *fmt, ...)
+	__attribute__((format (printf, 1, 2)));
+extern struct str *str_vprintf(const char *fmt, va_list args);
+
+/*
+ * struct val reference counting & casting to struct str/sym
+ */
+
 static inline bool val_isstatic(struct val *x)
 {
 	return x->static_struct;
 }
 
-REFCNT_INLINE_FXNS(struct val, val, refcnt, val_free, val_isstatic)
+REFCNT_INLINE_FXNS(struct val, val, refcnt, val_free, val_isstatic);
+
+#define val_cast_to_str(v)	((struct str *) _val_cast_to_strsym(v, VT_STR))
+#define val_cast_to_sym(v)	((struct sym *) _val_cast_to_strsym(v, VT_SYM))
+static inline struct val *_val_cast_to_strsym(struct val *val,
+					      enum val_type type)
+{
+	ASSERT(!val || (val->type == type));
+
+	return val_getref(val);
+}
+
+#define val_getref_str(v)	val_cast_to_str(val_getref(v))
+#define val_getref_sym(v)	val_cast_to_sym(val_getref(v))
+
+/*
+ * struct str/sym reference counting & casting to struct val
+ */
+
+#define str_getref(s)		((struct str *) val_getref(str_cast_to_val(s)))
+#define sym_getref(s)		((struct sym *) val_getref(sym_cast_to_val(s)))
 
-#define VAL_ALLOC_SYM(v)			\
-	({					\
-		struct val *_x;			\
-		_x = val_alloc_sym(v);		\
-		ASSERT(!IS_ERR(_x));		\
-		_x;				\
-	})
+#define str_putref(s)		val_putref(str_cast_to_val(s))
+#define sym_putref(s)		val_putref(sym_cast_to_val(s))
+
+#define str_getref_val(s)	val_getref(str_cast_to_val(s))
+#define sym_getref_val(s)	val_getref(sym_cast_to_val(s))
+
+static inline struct val *str_cast_to_val(struct str *str)
+{
+	return &str->val;
+}
+
+static inline struct val *sym_cast_to_val(struct sym *sym)
+{
+	return &sym->val;
+}
+
+/*
+ * struct val/str/sym casting to cstr
+ */
+
+static inline const char *val_cstr(const struct val *val)
+{
+	if (!val)
+		return NULL;
+
+	VERIFY((val->type == VT_STR) || (val->type == VT_SYM));
 
-#define VAL_ALLOC_SYM_CSTR(v)			\
-	VAL_ALLOC_SYM(STR_DUP(v))
+	return val->inline_alloc ? val->str_inline : val->str_ptr;
+}
+
+static inline const char *str_cstr(const struct str *str)
+{
+	return val_cstr(&str->val);
+}
+
+static inline const char *sym_cstr(const struct sym *sym)
+{
+	return val_cstr(&sym->val);
+}
+
+/*
+ * Initializers
+ */
 
-#define VAL_ALLOC_STR(v)			\
-	({					\
-		struct val *_x;			\
-		_x = val_alloc_str(v);		\
-		ASSERT(!IS_ERR(_x));		\
-		_x;				\
+#define __STATIC_CHAR_INITIALIZER(t, v)			\
+	{						\
+		.val = {				\
+			.type = (t),			\
+			.str_inline = { (v), '\0' },	\
+			.static_struct = true,		\
+			.static_alloc = true,		\
+			.inline_alloc = true,		\
+		}					\
+	}
+#define STR_STATIC_CHAR_INITIALIZER(v)			\
+	__STATIC_CHAR_INITIALIZER(VT_STR, v)
+#define SYM_STATIC_CHAR_INITIALIZER(v)			\
+	__STATIC_CHAR_INITIALIZER(VT_SYM, v)
+
+#define _STATIC_STR_INITIALIZER(t, v)			\
+	{						\
+		.val = {				\
+			.type = (t),			\
+			.str_ptr = (v),			\
+			.static_struct = true,		\
+			.static_alloc = true,		\
+		}					\
+	}
+#define STR_STATIC_INITIALIZER(v)			\
+		_STATIC_STR_INITIALIZER(VT_STR, (v))
+#define SYM_STATIC_INITIALIZER(v)			\
+		_STATIC_STR_INITIALIZER(VT_SYM, (v))
+
+/* evaluates to a struct str *, so it can be used as a value */
+#define STATIC_STR(s)					\
+	({						\
+		static struct str _s = STR_STATIC_INITIALIZER(s); \
+		&_s;					\
 	})
 
-#define VAL_ALLOC_CSTR(v)			\
-	VAL_ALLOC_STR(STR_ALLOC(v))
+/* evaluates to a struct sym *, so it can be used as a value */
+#define STATIC_SYM(s)					\
+	({						\
+		static struct sym _s = SYM_STATIC_INITIALIZER(s); \
+		&_s;					\
+	})
 
-#define VAL_ALLOC_CHAR(v)			\
-	({					\
-		struct val *_x;			\
-		_x = val_alloc_char(v);		\
-		ASSERT(!IS_ERR(_x));		\
-		_x;				\
+/*
+ * Allocation wrappers.  These call the identically named functions and
+ * assert that there was no error.
+ */
+
+#define _VAL_ALLOC(type, alloc)	\
+	({				\
+		type *_s;		\
+		_s = alloc;		\
+		ASSERT(!IS_ERR(_s));	\
+		_s;			\
 	})
 
-#define VAL_ALLOC_INT(v)			\
-	({					\
-		struct val *_x;			\
-		_x = val_alloc_int(v);		\
-		ASSERT(!IS_ERR(_x));		\
-		_x;				\
-	})
+#define VAL_ALLOC_CHAR(v)	_VAL_ALLOC(struct val, val_alloc_char(v))
+#define VAL_ALLOC_INT(v)	_VAL_ALLOC(struct val, val_alloc_int(v))
+#define VAL_ALLOC_BOOL(v)	val_alloc_bool(v) /* never fails */
+#define VAL_ALLOC_NULL(v)	val_alloc_null(v) /* never fails */
+#define VAL_ALLOC_CONS(h, t)	_VAL_ALLOC(struct val, val_alloc_cons((h), (t)))
 
-/* never fails */
-#define VAL_ALLOC_BOOL(v)	val_alloc_bool(v)
-#define VAL_ALLOC_NULL(v)	val_alloc_null(v)
-
-#define VAL_ALLOC_CONS(head, tail)		\
-	({					\
-		struct val *_x;			\
-		_x = val_alloc_cons((head), (tail));\
-		ASSERT(!IS_ERR(_x));		\
-		_x;				\
-	})
-
-#define VAL_DUP_CSTR(v)			VAL_ALLOC_STR(STR_DUP(v))
+#define STR_ALLOC(s)		_VAL_ALLOC(struct str, str_alloc(s))
+#define SYM_ALLOC(s)		_VAL_ALLOC(struct sym, sym_alloc(s))
+#define VAL_ALLOC_STR(s)	str_cast_to_val(STR_ALLOC(s))
+#define VAL_ALLOC_SYM(s)	sym_cast_to_val(SYM_ALLOC(s))
+#define STR_ALLOC_STATIC(s)	_VAL_ALLOC(struct str, str_alloc_static(s))
+#define SYM_ALLOC_STATIC(s)	_VAL_ALLOC(struct sym, sym_alloc_static(s))
+#define VAL_ALLOC_STR_STATIC(s)	str_cast_to_val(str_alloc_static(s))
+#define VAL_ALLOC_SYM_STATIC(s)	sym_cast_to_val(sym_alloc_static(s))
+#define STR_DUP(s)		_VAL_ALLOC(struct str, str_dup(s))
+#define SYM_DUP(s)		_VAL_ALLOC(struct sym, sym_dup(s))
+#define VAL_DUP_STR(s)		str_cast_to_val(str_dup(s))
+#define VAL_DUP_SYM(s)		sym_cast_to_val(sym_dup(s))
+#define STR_DUP_LEN(s, l)	_VAL_ALLOC(struct str, str_dup_len((s), (l)))
+#define SYM_DUP_LEN(s, l)	_VAL_ALLOC(struct sym, sym_dup_len((s), (l)))
+#define VAL_DUP_STR_LEN(s, l)	str_cast_to_val(str_dup_len((s), (l)))
+#define VAL_DUP_SYM_LEN(s, l)	sym_cast_to_val(sym_dup_len((s), (l)))
 
 #endif
--- a/jeffpc.mapfile-vers	Tue Aug 15 22:55:36 2017 +0300
+++ b/jeffpc.mapfile-vers	Tue Aug 15 22:38:16 2017 +0300
@@ -152,16 +152,15 @@
 		# sock
 		connect_ip;
 
-		# str
-		str_alloc;
-		str_alloc_static;
+		# str / sym
+		_strsym_alloc;
+		_strsym_alloc_static;
+		_strsym_cmp;
+		_strsym_dup;
+		_strsym_dup_len;
+		_strsym_len;
 		str_cat;
-		str_cmp;
-		str_dup;
-		str_dup_len;
 		str_empty_string;
-		str_free;
-		str_len;
 		str_printf;
 		str_vprintf;
 
@@ -206,8 +205,6 @@
 		val_alloc_cons;
 		val_alloc_int;
 		val_alloc_null;
-		val_alloc_str;
-		val_alloc_sym;
 
 		# urldecode
 		urldecode;
--- a/sexpr.c	Tue Aug 15 22:55:36 2017 +0300
+++ b/sexpr.c	Tue Aug 15 22:38:16 2017 +0300
@@ -25,7 +25,6 @@
 #include <string.h>
 #include <ctype.h>
 
-#include <jeffpc/str.h>
 #include <jeffpc/sexpr.h>
 
 #include "sexpr_impl.h"
@@ -196,20 +195,17 @@
 	static struct str cparen = STR_STATIC_CHAR_INITIALIZER(')');
 	static struct str empty = STR_STATIC_INITIALIZER("()");
 	struct str *tmp;
-	char *tmpstr;
 
 	if (!lv)
 		return &empty;
 
 	switch (lv->type) {
 		case VT_SYM:
-			return str_getref(lv->str);
+			return str_dup(val_cstr(lv));
 		case VT_STR:
-			tmpstr = escape_str(str_cstr(lv->str));
-
-			tmp = str_alloc(tmpstr);
-			if (!tmp)
-				return ERR_PTR(-ENOMEM);
+			tmp = str_alloc(escape_str(val_cstr(lv)));
+			if (IS_ERR(tmp))
+				return tmp;
 
 			return str_cat(3, &dquote, tmp, &dquote);
 		case VT_NULL:
@@ -229,7 +225,7 @@
 
 			/* handle quoting */
 			if (!raw && head && (head->type == VT_SYM) &&
-			    !strcmp(str_cstr(head->str), "quote")) {
+			    !strcmp(val_cstr(head), "quote")) {
 				/* we're dealing with a (quote) */
 				if (sexpr_is_null(tail))
 					return str_cat(2, &squote, &empty);
@@ -471,7 +467,7 @@
 	    head->cons.head &&
 	    ((head->cons.head->type == VT_STR) ||
 	     (head->cons.head->type == VT_SYM)) &&
-	    !strcmp(str_cstr(head->cons.head->str), name))
+	    !strcmp(val_cstr(head->cons.head), name))
 		return val_getref(head);
 
 	return sexpr_assoc(tail, name);
@@ -524,7 +520,8 @@
 			goto out;
 		case VT_STR:
 		case VT_SYM:
-			ret = str_cmp(lhs->str, rhs->str) == 0;
+			ret = str_cmp(val_cast_to_str(lhs),
+				      val_cast_to_str(rhs)) == 0;
 			goto out;
 		case VT_BOOL:
 			ret = (lhs->b == rhs->b);
@@ -565,7 +562,7 @@
 	if (!v || (v->type != VT_STR))
 		ret = NULL;
 	else
-		ret = str_getref(v->str);
+		ret = val_cast_to_str(val_getref(v));
 
 	val_putref(v);
 
--- a/sexpr.y	Tue Aug 15 22:55:36 2017 +0300
+++ b/sexpr.y	Tue Aug 15 22:38:16 2017 +0300
@@ -66,15 +66,15 @@
 document : tok			{ data->output = $1; }
 	 ;
 
-tok : SYMBOL			{ $$ = VAL_ALLOC_SYM($1); }
-    | STRING			{ $$ = VAL_ALLOC_STR($1); }
+tok : SYMBOL			{ $$ = str_cast_to_val($1); }
+    | STRING			{ $$ = str_cast_to_val($1); }
     | NUMBER			{ $$ = VAL_ALLOC_INT($1); }
     | BOOL			{ $$ = VAL_ALLOC_BOOL($1); }
     | CHAR			{ $$ = VAL_ALLOC_CHAR($1); }
     | VNULL			{ $$ = VAL_ALLOC_NULL(); }
     | list			{ $$ = $1; }
     | '\'' tok			{ $$ = VAL_ALLOC_CONS(
-				         VAL_ALLOC_SYM_CSTR("quote"),
+                                         sym_cast_to_val(STATIC_SYM("quote")),
 				         VAL_ALLOC_CONS($2, NULL)); }
     ;
 
--- a/sexpr_eval.c	Tue Aug 15 22:55:36 2017 +0300
+++ b/sexpr_eval.c	Tue Aug 15 22:38:16 2017 +0300
@@ -155,12 +155,12 @@
 	{ NULL, },
 };
 
-static struct builtin_fxn *fxnlookup_builtin(struct str *name)
+static struct builtin_fxn *fxnlookup_builtin(struct sym *name)
 {
 	size_t i;
 
 	for (i = 0; builtins[i].name; i++)
-		if (!strcmp(builtins[i].name, str_cstr(name)))
+		if (!strcmp(builtins[i].name, sym_cstr(name)))
 			return &builtins[i];
 
 	return NULL;
@@ -169,7 +169,7 @@
 static struct val *eval_cons(struct val *expr, struct sexpr_eval_env *env)
 {
 	struct builtin_fxn *fxn;
-	struct str *name;
+	struct sym *name;
 	struct val *args;
 	struct val *op;
 
@@ -188,7 +188,7 @@
 			      op->i);
 		case VT_STR:
 			panic("function name cannot be a VT_STR (\"%s\")",
-			      str_cstr(op->str));
+			      val_cstr(op));
 		case VT_BOOL:
 			panic("function name cannot be a VT_BOOL (%s)",
 			      op->b ? "true" : "false");
@@ -198,8 +198,7 @@
 			break; /* ok */
 	}
 
-	name = str_getref(op->str);
-	val_putref(op);
+	name = val_cast_to_sym(op);
 
 	if (env->fxnlookup) {
 		fxn = env->fxnlookup(name, env);
@@ -211,10 +210,10 @@
 	if (fxn)
 		goto found;
 
-	panic("unknown function '%s'", str_cstr(name));
+	panic("unknown function '%s'", sym_cstr(name));
 
 found:
-	str_putref(name);
+	sym_putref(name);
 
 	if (fxn->arglen != -1) {
 		size_t got;
@@ -248,22 +247,16 @@
 		case VT_BOOL:
 		case VT_CHAR:
 			return expr;
-		case VT_SYM: {
-			struct str *name;
-
+		case VT_SYM:
 			if (!env->symlookup)
 				panic("VT_SYM requires non-NULL symlookup "
 				      "function in the environment");
 
-			name = str_getref(expr->str);
-			val_putref(expr);
-
 			/*
 			 * Symbol lookup returns a value (not an expression)
 			 * therefore we don't want to evaluate it.
 			 */
-			return env->symlookup(name, env);
-		}
+			return env->symlookup(val_cast_to_sym(expr), env);
 		case VT_CONS:
 			return eval_cons(expr, env);
 	}
--- a/str.c	Tue Aug 15 22:55:36 2017 +0300
+++ b/str.c	Tue Aug 15 22:38:16 2017 +0300
@@ -24,19 +24,29 @@
 #include <stdbool.h>
 #include <alloca.h>
 
-#include <jeffpc/str.h>
 #include <jeffpc/mem.h>
 #include <jeffpc/jeffpc.h>
 
-/* check that STR_INLINE_LEN is used properly in the struct str definition */
-STATIC_ASSERT(STR_INLINE_LEN + 1 == sizeof(((struct str *) NULL)->inline_str));
+#include "val_impl.h"
+
+/* check that STR_INLINE_LEN is used properly in the struct definition */
+STATIC_ASSERT(STR_INLINE_LEN + 1 == sizeof(((struct str *) NULL)->val.str_inline));
 
 /* check that STR_INLINE_LEN is not undersized */
 STATIC_ASSERT(STR_INLINE_LEN + 1 >= sizeof(char *));
 
+/* check that struct str is just a type alias for struct val */
+STATIC_ASSERT(sizeof(struct str) == sizeof(struct val));
+STATIC_ASSERT(offsetof(struct str, val) == 0);
+
+/* same as above but for struct sym */
+STATIC_ASSERT(sizeof(struct sym) == sizeof(struct val));
+STATIC_ASSERT(offsetof(struct sym, val) == 0);
+
 #define USE_STRLEN	((size_t) ~0ul)
 
-static struct str empty_string = _STR_STATIC_INITIALIZER("", 0);
+static struct str empty_string = _STATIC_STR_INITIALIZER(VT_STR, "");
+static struct sym empty_symbol = _STATIC_STR_INITIALIZER(VT_SYM, "");
 
 /* one 7-bit ASCII character long strings */
 static struct str one_char[128] = {
@@ -64,52 +74,35 @@
 	STATIC_CHAR32(96)	/* 96..127 */
 };
 
-static struct mem_cache *str_cache;
-
-static inline size_t str_get_len(const struct str *str)
+static inline size_t get_len(const struct val *val)
 {
-	if (!str->have_len)
-		return strlen(str_cstr(str));
-
-	return (str->len[0] << 16) | (str->len[1] << 8) | str->len[2];
+	return strlen(val_cstr(val));
 }
 
-static inline void str_set_len(struct str *str, size_t len)
+static inline void set_len(struct val *str, size_t len)
 {
-	if (len > 0xffffff) {
-		str->have_len = false;
-		str->len[0] = 0xff;
-		str->len[1] = 0xff;
-		str->len[2] = 0xff;
-	} else {
-		str->have_len = true;
-		str->len[0] = (len >> 16) & 0xff;
-		str->len[1] = (len >> 8) & 0xff;
-		str->len[2] = len & 0xff;
-	}
+	/* TODO: stash length in struct val if possible */
 }
 
-static void __attribute__((constructor)) init_str_subsys(void)
-{
-	str_cache = mem_cache_create("str-cache", sizeof(struct str), 0);
-	ASSERT(!IS_ERR(str_cache));
-}
-
-static struct str *__get_preallocated(const char *s, size_t len)
+static struct val *__get_preallocated(enum val_type type, const char *s,
+				      size_t len)
 {
 	unsigned char first_char;
 
 	/* NULL or non-nul terminated & zero length */
 	if (!s || !len)
-		return &empty_string;
+		return (type == VT_STR) ? &empty_string.val : &empty_symbol.val;
+
+	if (type != VT_STR)
+		return NULL;
 
 	first_char = s[0];
 
 	/* preallocated one-char long strings of 7-bit ASCII */
 	if ((len == 1) &&
 	    (first_char > '\0') && (first_char < '\x7f') &&
-	    one_char[first_char].static_struct)
-		return &one_char[first_char];
+	    one_char[first_char].val.static_struct)
+		return &one_char[first_char].val;
 
 	/* nothing pre-allocated */
 	return NULL;
@@ -126,12 +119,14 @@
 	dest[len] = '\0';
 }
 
-static struct str *__alloc(const char *s, size_t len, bool heapalloc,
-			   bool mustdup)
+static struct val *__alloc(enum val_type type, const char *s, size_t len,
+			   bool heapalloc, bool mustdup)
 {
-	struct str *str;
+	struct val *val;
 	bool copy;
 
+	ASSERT((type == VT_STR) || (type == VT_SYM));
+
 	/* sanity check */
 	if (mustdup)
 		ASSERT(!heapalloc);
@@ -140,8 +135,8 @@
 	len = s ? strnlen(s, len) : 0;
 
 	/* check preallocated strings */
-	str = __get_preallocated(s, len);
-	if (str)
+	val = __get_preallocated(type, s, len);
+	if (val)
 		goto out;
 
 	/* can we inline it? */
@@ -152,8 +147,10 @@
 		char *tmp;
 
 		tmp = malloc(len + 1);
-		if (!tmp)
+		if (!tmp) {
+			val = ERR_PTR(-ENOMEM);
 			goto out;
+		}
 
 		__copy(tmp, s, len);
 
@@ -162,78 +159,66 @@
 		s = tmp;
 	}
 
-	str = mem_cache_alloc(str_cache);
-	if (!str)
+	val = __val_alloc(type);
+	if (IS_ERR(val))
 		goto out;
 
-	refcnt_init(&str->refcnt, 1);
-	str->static_struct = false;
-	str->static_alloc = copy || !heapalloc;
-	str->inline_alloc = copy;
-	str_set_len(str, len);
+	val->static_alloc = copy || !heapalloc;
+	val->inline_alloc = copy;
+	set_len(val, len);
 
 	if (copy) {
-		__copy(str->inline_str, s, len);
+		__copy(val->_set_str_inline, s, len);
 
 		if (heapalloc)
 			free((char *) s);
 	} else {
-		str->str = s;
+		val->_set_str_ptr = s;
 	}
 
-	return str;
+	return val;
 
 out:
 	if (heapalloc)
 		free((char *) s);
 
-	return str;
+	return val;
 }
 
-/*
- * Passed in str cannot be freed, and it must be dup'd.  (E.g., it could be
- * a string on the stack.)
- */
-struct str *str_dup(const char *s)
+struct val *_strsym_dup(const char *s, enum val_type type)
 {
-	return __alloc(s, USE_STRLEN, false, true);
+	return __alloc(type, s, USE_STRLEN, false, true);
 }
 
-struct str *str_dup_len(const char *s, size_t len)
+struct val *_strsym_dup_len(const char *s, size_t len, enum val_type type)
 {
-	return __alloc(s, len, false, true);
+	return __alloc(type, s, len, false, true);
 }
 
-/* Passed in str must be freed. */
-struct str *str_alloc(char *s)
+struct val *_strsym_alloc(char *s, enum val_type type)
 {
-	return __alloc(s, USE_STRLEN, true, false);
+	return __alloc(type, s, USE_STRLEN, true, false);
 }
 
-/*
- * Passed in str cannot be freed, and it doesn't have to be dup'd.  (E.g.,
- * it could be a string in .rodata.)
- */
-struct str *str_alloc_static(const char *s)
+struct val *_strsym_alloc_static(const char *s, enum val_type type)
 {
-	return __alloc(s, USE_STRLEN, false, false);
+	return __alloc(type, s, USE_STRLEN, false, false);
 }
 
-size_t str_len(const struct str *s)
+size_t _strsym_len(const struct val *s)
 {
-	return str_get_len(s);
+	return get_len(s);
 }
 
-int str_cmp(const struct str *a, const struct str *b)
+int _strsym_cmp(const struct val *a, const struct val *b)
 {
-	return strcmp(str_cstr(a), str_cstr(b));
+	return strcmp(val_cstr(a), val_cstr(b));
 }
 
 struct str *str_cat(size_t n, ...)
 {
 	const size_t nargs = n;
 	size_t totallen;
-	struct str *ret;
 	char *buf, *out;
 	size_t *len;
 	va_list ap;
@@ -243,9 +228,18 @@
 		return NULL;
 
 	if (nargs == 1) {
+		struct val *val;
+		struct str *ret;
+
 		va_start(ap, n);
-		ret = va_arg(ap, struct str *);
+		val = va_arg(ap, struct val *);
 		va_end(ap);
+
+		if (val->type == VT_STR)
+			return val_cast_to_str(val);
+
+		ret = STR_DUP(val_cstr(val));
+		val_putref(val);
 		return ret;
 	}
 
@@ -254,9 +248,14 @@
 
 	va_start(ap, n);
 	for (i = 0; i < nargs; i++) {
-		struct str *str = va_arg(ap, struct str *);
+		struct val *val = va_arg(ap, struct val *);
 
-		len[i] = str ? str_get_len(str) : 0;
+		if (!val) {
+			len[i] = 0;
+			continue;
+		}
+
+		len[i] = get_len(val);
 
 		totallen += len[i];
 	}
@@ -271,16 +270,16 @@
 
 	va_start(ap, n);
 	for (i = 0; i < nargs; i++) {
-		struct str *str = va_arg(ap, struct str *);
+		struct val *val = va_arg(ap, struct val *);
 
-		if (!str)
+		if (!val)
 			continue;
 
-		strcpy(out, str_cstr(str));
+		strcpy(out, val_cstr(val));
 
 		out += len[i];
 
-		str_putref(str);
+		val_putref(val);
 	}
 	va_end(ap);
 
@@ -313,16 +312,6 @@
 	return ret;
 }
 
-void str_free(struct str *str)
-{
-	ASSERT(str);
-	ASSERT3U(refcnt_read(&str->refcnt), ==, 0);
-
-	if (!str->inline_alloc && !str->static_alloc)
-		free((char *) str->str);
-	mem_cache_free(str_cache, str);
-}
-
 struct str *str_empty_string(void)
 {
 	return &empty_string;
--- a/tests/test_sexpr_eval.c	Tue Aug 15 22:55:36 2017 +0300
+++ b/tests/test_sexpr_eval.c	Tue Aug 15 22:38:16 2017 +0300
@@ -41,7 +41,7 @@
 	struct val *arr[1];
 	struct val *ret;
 
-	arr[0] = VAL_ALLOC_SYM_CSTR(op);
+	arr[0] = VAL_ALLOC_SYM_STATIC(op);
 
 	ret = sexpr_array_to_list(arr, ARRAY_LEN(arr));
 
@@ -53,7 +53,7 @@
 	struct val *arr[2];
 	struct val *ret;
 
-	arr[0] = VAL_ALLOC_SYM_CSTR(op);
+	arr[0] = VAL_ALLOC_SYM_STATIC(op);
 	arr[1] = a;
 
 	ret = sexpr_array_to_list(arr, ARRAY_LEN(arr));
@@ -66,7 +66,7 @@
 	struct val *arr[3];
 	struct val *ret;
 
-	arr[0] = VAL_ALLOC_SYM_CSTR(op);
+	arr[0] = VAL_ALLOC_SYM_STATIC(op);
 	arr[1] = a;
 	arr[2] = b;
 
@@ -80,7 +80,7 @@
 	struct val *arr[4];
 	struct val *ret;
 
-	arr[0] = VAL_ALLOC_SYM_CSTR(op);
+	arr[0] = VAL_ALLOC_SYM_STATIC(op);
 	arr[1] = a;
 	arr[2] = b;
 	arr[3] = c;
--- a/tests/test_sexpr_parser.c	Tue Aug 15 22:55:36 2017 +0300
+++ b/tests/test_sexpr_parser.c	Tue Aug 15 22:38:16 2017 +0300
@@ -24,7 +24,6 @@
 
 #include <jeffpc/error.h>
 #include <jeffpc/sexpr.h>
-#include <jeffpc/str.h>
 #include <jeffpc/val.h>
 #include <jeffpc/io.h>
 
--- a/val.c	Tue Aug 15 22:55:36 2017 +0300
+++ b/val.c	Tue Aug 15 22:38:16 2017 +0300
@@ -96,7 +96,8 @@
 			break;
 		case VT_STR:
 		case VT_SYM:
-			str_putref(val->str);
+			if (!val->static_alloc)
+				free((char *) val->_set_str_ptr);
 			break;
 		case VT_CONS:
 			val_putref(val->cons.head);
@@ -124,8 +125,6 @@
 }
 
 static DEF_VAL_SET(int_heap, VT_INT, i, uint64_t, (void))
-DEF_VAL_SET(str, VT_STR, str, struct str *, str_putref)
-DEF_VAL_SET(sym, VT_SYM, str, struct str *, str_putref)
 DEF_VAL_SET(char, VT_CHAR, i, uint64_t, (void))
 
 struct val *val_alloc_int(uint64_t i)
@@ -211,7 +210,7 @@
 			break;
 		case VT_STR:
 		case VT_SYM:
-			fprintf(out, "%*s'%s'\n", indent, "", str_cstr(val->str));
+			fprintf(out, "%*s'%s'\n", indent, "", val_cstr(val));
 			break;
 		case VT_INT:
 			fprintf(out, "%*s%"PRIu64"\n", indent, "", val->i);