Mercurial > libjeffpc
view include/jeffpc/val.h @ 824:562d1c61194e
val: add INT_STATIC_INITIALIZER and STATIC_INT macros
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Sat, 26 Dec 2020 10:43:42 -0500 |
parents | 2c72761bc044 |
children | 93d6c996250f |
line wrap: on
line source
/* * Copyright (c) 2014-2020 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef __JEFFPC_VAL_H #define __JEFFPC_VAL_H #include <stdint.h> #include <stdbool.h> #include <jeffpc/int.h> #include <jeffpc/refcnt.h> #include <jeffpc/error.h> #include <jeffpc/types.h> #include <jeffpc/list.h> #include <jeffpc/rbtree.h> /* * A typed value structure. * * A struct val can hold a number of different values along with type * information. There are several different types that are supported. * * Note: The sexpr code considers NULL the same as an empty VT_CONS. This * is different from VT_NULL. * * Note: Not all consumers of struct val use all types or can reliably * serialize and deserialize all types. */ enum val_type { VT_NULL = 0, /* not a value */ VT_INT, /* 64-bit uint */ VT_STR, /* a struct str string */ VT_SYM, /* symbol */ VT_BOOL, /* boolean */ VT_CONS, /* cons cell */ VT_CHAR, /* a single (unicode) character */ VT_BLOB, /* a byte string */ VT_ARRAY, /* an array of values */ VT_NVL, /* an nvlist */ }; /* serialization formats */ enum val_format { VF_CBOR, /* RFC 7049 */ VF_JSON, /* RFC 7159 */ }; #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 { union { const char *ptr; const char inline_str[STR_INLINE_LEN + 1]; }; } str; const struct { struct val *head; struct val *tail; } cons; const struct { const void *ptr; size_t size; } blob; const struct { struct val **vals; size_t nelem; } array; const struct { struct rb_tree values; } nvl; /* * We want to keep the normal members const to catch * attempts to modify them, but at the same time we need to * initialize them after allocation. Instead of venturing * into undefined behavior territory full of ugly casts, we * just duplicate the above members without the const and * with much uglier name. * * Do not use the following members unless you are the val * allocation function! */ uint64_t _set_i; bool _set_b; const char *_set_str_ptr; char _set_str_inline[STR_INLINE_LEN + 1]; struct { struct val *head; struct val *tail; } _set_cons; struct { void *ptr; size_t size; } _set_blob; struct { struct val **vals; size_t nelem; } _set_array; struct { struct rb_tree values; } _set_nvl; }; }; /* ref-counted string */ struct str { struct val val; }; /* ref-counted symbol name */ struct sym { struct val val; }; /* * Allocation functions */ extern struct val *val_alloc_blob(void *ptr, size_t size); extern struct val *val_alloc_blob_dup(const void *ptr, size_t size); extern struct val *val_alloc_blob_static(const void *ptr, size_t size); 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); extern struct val *val_alloc_nvl(void); /* val_alloc_cons always consume the passed in references */ extern struct val *val_alloc_cons(struct val *head, struct val *tail); /* assorted preallocated values */ extern struct val *val_empty_cons(void); /* * The passed in struct val references are always consumed. * * val_alloc_array takes consumes the heap allocated vals array * val_alloc_array_dup duplicates the vals array * val_alloc_array_static uses the vals array as-is */ extern struct val *val_alloc_array(struct val **vals, size_t nelem); extern struct val *val_alloc_array_dup(struct val **vals, size_t nelem); extern struct val *val_alloc_array_static(struct val **vals, size_t nelem); /* * 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); /* * Serialization functions */ extern ssize_t val_size(struct val *val, enum val_format format); extern struct buffer *val_pack(struct val *val, enum val_format format); extern ssize_t val_pack_into(struct val *val, void *buf, size_t len, enum val_format format); extern struct val *val_unpack(const void *ptr, size_t len, enum val_format format); /* * Dumping functions */ extern const char *__val_typename(int type, bool human); extern void val_dump_file(FILE *out, struct val *v, int indent); static inline void val_dump(struct val *v, int indent) { val_dump_file(stderr, v, indent); } /* * NOTE: The typename functions below may return a thread-local buffer when * given an unknown type. */ static inline const char *val_typename(int type) { return __val_typename(type, true); } static inline const char *val_rawtypename(int type) { return __val_typename(type, false); } /* * 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); #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; } #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 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)); return val->inline_alloc ? val->str.inline_str : 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 __STATIC_CHAR_INITIALIZER(t, v) \ { \ .val = { \ .type = (t), \ .str.inline_str = { (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)) #define INT_STATIC_INITIALIZER(v) \ { \ .type = VT_INT, \ .static_struct = true, \ .i = (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; \ }) /* 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; \ }) /* evaluates to a struct val *, so it can be used as a value */ #define STATIC_INT(v) \ ({ \ static struct val _v = INT_STATIC_INITIALIZER(v); \ &_v; \ }) /* * 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_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() val_alloc_null() /* never fails */ #define VAL_ALLOC_CONS(h, t) _VAL_ALLOC(struct val, val_alloc_cons((h), (t))) #define VAL_ALLOC_NVL() _VAL_ALLOC(struct val, val_alloc_nvl()) #define VAL_ALLOC_ARRAY(v, l) _VAL_ALLOC(struct val, val_alloc_array((v), (l))) #define VAL_ALLOC_ARRAY_DUP(v, l) _VAL_ALLOC(struct val, val_alloc_array_dup((v), (l))) #define VAL_ALLOC_ARRAY_STATIC(v, l) _VAL_ALLOC(struct val, val_alloc_array_static((v), (l))) #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