1 /*
2  * SPDX-License-Identifier: ISC
3  *
4  * Copyright (C) 2017-2021 Michael Drake <tlsa@netsurf-browser.org>
5  */
6 
7 /**
8  * \file
9  * \brief CYAML common utility functions.
10  */
11 
12 #ifndef CYAML_UTIL_H
13 #define CYAML_UTIL_H
14 
15 #include "cyaml/cyaml.h"
16 #include "utf8.h"
17 
18 /** Macro to squash unused variable compiler warnings. */
19 #define CYAML_UNUSED(_x) ((void)(_x))
20 
21 /**
22  * Check whether the host is little endian.
23  *
24  * Checks whether least significant bit is in the first byte of a `uint16_t`.
25  *
26  * \return true if host is little endian.
27  */
cyaml__host_is_little_endian(void)28 static inline bool cyaml__host_is_little_endian(void)
29 {
30 	static const uint16_t test = 1;
31 
32 	return ((const uint8_t *) &test)[0] == 1;
33 }
34 
35 /**
36  * Check whether the host is big endian.
37  *
38  * \return true if host is big endian.
39  */
cyaml__host_is_big_endian(void)40 static inline bool cyaml__host_is_big_endian(void)
41 {
42 	return !cyaml__host_is_little_endian();
43 }
44 
45 /** CYAML bitfield type. */
46 typedef uint32_t cyaml_bitfield_t;
47 
48 /** Number of bits in \ref cyaml_bitfield_t. */
49 #define CYAML_BITFIELD_BITS (sizeof(cyaml_bitfield_t) * CHAR_BIT)
50 
51 /** CYAML state machine states. */
52 enum cyaml_state_e {
53 	CYAML_STATE_START,        /**< Initial state. */
54 	CYAML_STATE_IN_STREAM,    /**< In a stream. */
55 	CYAML_STATE_IN_DOC,       /**< In a document. */
56 	CYAML_STATE_IN_MAP_KEY,   /**< In a mapping. */
57 	CYAML_STATE_IN_MAP_VALUE, /**< In a mapping. */
58 	CYAML_STATE_IN_SEQUENCE,  /**< In a sequence. */
59 	CYAML_STATE__COUNT,       /**< Count of states, **not a valid
60 	                               state itself**. */
61 };
62 
63 /**
64  * Convert a CYAML state into a human readable string.
65  *
66  * \param[in]  state  The state to convert.
67  * \return String representing state.
68  */
cyaml__state_to_str(enum cyaml_state_e state)69 static inline const char * cyaml__state_to_str(enum cyaml_state_e state)
70 {
71 	static const char * const strings[CYAML_STATE__COUNT] = {
72 		[CYAML_STATE_START]        = "start",
73 		[CYAML_STATE_IN_STREAM]    = "in stream",
74 		[CYAML_STATE_IN_DOC]       = "in doc",
75 		[CYAML_STATE_IN_MAP_KEY]   = "in mapping (key)",
76 		[CYAML_STATE_IN_MAP_VALUE] = "in mapping (value)",
77 		[CYAML_STATE_IN_SEQUENCE]  = "in sequence",
78 	};
79 	if ((unsigned)state >= CYAML_STATE__COUNT) {
80 		return "<invalid>";
81 	}
82 	return strings[state];
83 }
84 
85 /**
86  * Convert a CYAML type into a human readable string.
87  *
88  * \param[in]  type  The state to convert.
89  * \return String representing state.
90  */
cyaml__type_to_str(cyaml_type_e type)91 static inline const char * cyaml__type_to_str(cyaml_type_e type)
92 {
93 	static const char * const strings[CYAML__TYPE_COUNT] = {
94 		[CYAML_INT]            = "INT",
95 		[CYAML_UINT]           = "UINT",
96 		[CYAML_BOOL]           = "BOOL",
97 		[CYAML_ENUM]           = "ENUM",
98 		[CYAML_FLAGS]          = "FLAGS",
99 		[CYAML_FLOAT]          = "FLOAT",
100 		[CYAML_STRING]         = "STRING",
101 		[CYAML_MAPPING]        = "MAPPING",
102 		[CYAML_BITFIELD]       = "BITFIELD",
103 		[CYAML_SEQUENCE]       = "SEQUENCE",
104 		[CYAML_SEQUENCE_FIXED] = "SEQUENCE_FIXED",
105 		[CYAML_IGNORE]         = "IGNORE",
106 	};
107 	if ((unsigned)type >= CYAML__TYPE_COUNT) {
108 		return "<invalid>";
109 	}
110 	return strings[type];
111 }
112 
113 /**
114  * Log to client's logging function, if provided.
115  *
116  * \param[in] cfg    CYAML client config structure.
117  * \param[in] level  Log level of message to log.
118  * \param[in] fmt    Format string for message to log.
119  * \param[in] ...    Additional arguments used by fmt.
120  */
cyaml__log(const cyaml_config_t * cfg,cyaml_log_t level,const char * fmt,...)121 static inline void cyaml__log(
122 		const cyaml_config_t *cfg,
123 		cyaml_log_t level,
124 		const char *fmt, ...)
125 {
126 	if (level >= cfg->log_level && cfg->log_fn != NULL) {
127 		va_list args;
128 		va_start(args, fmt);
129 		cfg->log_fn(level, cfg->log_ctx, fmt, args);
130 		va_end(args);
131 	}
132 }
133 
134 /**
135  * Check if comparason should be case sensitive.
136  *
137  * As described in the API, schema flags take priority over config flags.
138  *
139  * \param[in]  config  Client's CYAML configuration structure.
140  * \param[in]  schema  The CYAML schema for the value to be compared.
141  * \return Whether to use case-sensitive comparason.
142  */
cyaml__is_case_sensitive(const cyaml_config_t * config,const cyaml_schema_value_t * schema)143 static inline bool cyaml__is_case_sensitive(
144 		const cyaml_config_t *config,
145 		const cyaml_schema_value_t *schema)
146 {
147 	if (schema->flags & CYAML_FLAG_CASE_INSENSITIVE) {
148 		return false;
149 
150 	} else if (schema->flags & CYAML_FLAG_CASE_SENSITIVE) {
151 		return true;
152 
153 	} else if (config->flags & CYAML_CFG_CASE_INSENSITIVE) {
154 		return false;
155 
156 	}
157 
158 	return true;
159 }
160 
161 /**
162  * Compare two strings.
163  *
164  * Depending on the client's configuration, and the value's schema,
165  * this will do either a case-sensitive or case-insensitive comparason.
166  *
167  * \param[in]  config  Client's CYAML configuration structure.
168  * \param[in]  schema  The CYAML schema for the value to be compared.
169  * \param[in]  str1    First string to be compared.
170  * \param[in]  str2    Second string to be compared.
171  * \return 0 if and only if strings are equal.
172  */
cyaml__strcmp(const cyaml_config_t * config,const cyaml_schema_value_t * schema,const void * const str1,const void * const str2)173 static inline int cyaml__strcmp(
174 		const cyaml_config_t *config,
175 		const cyaml_schema_value_t *schema,
176 		const void * const str1,
177 		const void * const str2)
178 {
179 	if (cyaml__is_case_sensitive(config, schema)) {
180 		return strcmp(str1, str2);
181 	}
182 
183 	return cyaml_utf8_casecmp(str1, str2);
184 }
185 
186 /**
187  * Check of all the bits of a mask are set in a cyaml value flag word.
188  *
189  * \param[in] flags  The value flags to test.
190  * \param[in] mask   Mask of the bits to test for in flags.
191  * \return true if all bits of mask are set in flags.
192  */
cyaml__flag_check_all(enum cyaml_flag flags,enum cyaml_flag mask)193 static inline bool cyaml__flag_check_all(
194 		enum cyaml_flag flags,
195 		enum cyaml_flag mask)
196 {
197 	return ((flags & mask) == mask);
198 }
199 
200 #endif
201