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