DEFINE_TRIVIAL_CLEANUP_FUNC(JsonSource*, json_source_unref);
+/* There are four kind of JsonVariant* pointers:
+ *
+ * 1. NULL
+ * 2. A 'regular' one, i.e. pointing to malloc() memory
+ * 3. A 'magic' one, i.e. one of the special JSON_VARIANT_MAGIC_XYZ values, that encode a few very basic values directly in the pointer.
+ * 4. A 'const string' one, i.e. a pointer to a const string.
+ *
+ * The four kinds of pointers can be discerned like this:
+ *
+ * Detecting #1 is easy, just compare with NULL. Detecting #3 is similarly easy: all magic pointers are below
+ * _JSON_VARIANT_MAGIC_MAX (which is pretty low, within the first memory page, which is special on Linux and other
+ * OSes, as it is a faulting page). In order to discern #2 and #4 we check the lowest bit. If it's off it's #2,
+ * otherwise #4. This makes use of the fact that malloc() will return "maximum aligned" memory, which definitely
+ * means the pointer is even. This means we can use the uneven pointers to reference static strings, as long as we
+ * make sure that all static strings used like this are aligned to 2 (or higher), and that we mask the bit on
+ * access. The JSON_VARIANT_STRING_CONST() macro encodes strings as JsonVariant* pointers, with the bit set. */
+
static bool json_variant_is_magic(const JsonVariant *v) {
if (!v)
return false;
return v < _JSON_VARIANT_MAGIC_MAX;
}
+static bool json_variant_is_const_string(const JsonVariant *v) {
+
+ if (v < _JSON_VARIANT_MAGIC_MAX)
+ return false;
+
+ /* A proper JsonVariant is aligned to whatever malloc() aligns things too, which is definitely not uneven. We
+ * hence use all uneven pointers as indicators for const strings. */
+
+ return (((uintptr_t) v) & 1) != 0;
+}
+
+static bool json_variant_is_regular(const JsonVariant *v) {
+
+ if (v < _JSON_VARIANT_MAGIC_MAX)
+ return false;
+
+ return (((uintptr_t) v) & 1) == 0;
+}
+
static JsonVariant *json_variant_dereference(JsonVariant *v) {
/* Recursively dereference variants that are references to other variants */
if (!v)
return NULL;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
return v;
if (!v->is_reference)
if (!v)
return 0;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
return 0;
return v->depth;
if (!v)
return NULL;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
return v;
if (v->source || v->line > 0 || v->column > 0)
assert(v);
assert(from);
- if (json_variant_is_magic(from))
+ if (!json_variant_is_regular(from))
return;
v->line = from->line;
static void json_variant_free_inner(JsonVariant *v) {
assert(v);
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
return;
json_source_unref(v->source);
JsonVariant *json_variant_ref(JsonVariant *v) {
if (!v)
return NULL;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
return v;
if (v->is_embedded)
JsonVariant *json_variant_unref(JsonVariant *v) {
if (!v)
return NULL;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
return NULL;
if (v->is_embedded)
return "";
if (json_variant_is_magic(v))
goto mismatch;
+ if (json_variant_is_const_string(v)) {
+ uintptr_t p = (uintptr_t) v;
+
+ assert((p & 1) != 0);
+ return (const char*) (p ^ 1U);
+ }
+
if (v->is_reference)
return json_variant_string(v->reference);
if (v->type != JSON_VARIANT_STRING)
return true;
if (v == JSON_VARIANT_MAGIC_FALSE)
return false;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
goto mismatch;
if (v->type != JSON_VARIANT_BOOLEAN)
goto mismatch;
v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED ||
v == JSON_VARIANT_MAGIC_ZERO_REAL)
return 0;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
goto mismatch;
if (v->is_reference)
return json_variant_integer(v->reference);
v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED ||
v == JSON_VARIANT_MAGIC_ZERO_REAL)
return 0;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
goto mismatch;
if (v->is_reference)
return json_variant_integer(v->reference);
v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED ||
v == JSON_VARIANT_MAGIC_ZERO_REAL)
return 0.0;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
goto mismatch;
if (v->is_reference)
return json_variant_real(v->reference);
v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED ||
v == JSON_VARIANT_MAGIC_ZERO_REAL)
return false;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
goto mismatch;
if (v->is_reference)
return json_variant_is_negative(v->reference);
if (!v)
return _JSON_VARIANT_TYPE_INVALID;
+ if (json_variant_is_const_string(v))
+ return JSON_VARIANT_STRING;
+
if (v == JSON_VARIANT_MAGIC_TRUE || v == JSON_VARIANT_MAGIC_FALSE)
return JSON_VARIANT_BOOLEAN;
if (rt == type)
return true;
+ /* If it's a const string, then it only can be a string, and if it is not, it's not */
+ if (json_variant_is_const_string(v))
+ return false;
+
/* All three magic zeroes qualify as integer, unsigned and as real */
if ((v == JSON_VARIANT_MAGIC_ZERO_INTEGER || v == JSON_VARIANT_MAGIC_ZERO_UNSIGNED || v == JSON_VARIANT_MAGIC_ZERO_REAL) &&
IN_SET(type, JSON_VARIANT_INTEGER, JSON_VARIANT_UNSIGNED, JSON_VARIANT_REAL, JSON_VARIANT_NUMBER))
if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY ||
v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
return 0;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
goto mismatch;
if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
goto mismatch;
if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY ||
v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
return NULL;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
goto mismatch;
if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
goto mismatch;
goto not_found;
if (v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
goto not_found;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
goto mismatch;
if (v->type != JSON_VARIANT_OBJECT)
goto mismatch;
assert_return(v, -EINVAL);
if (ret_source)
- *ret_source = json_variant_is_magic(v) || !v->source ? NULL : v->source->name;
+ *ret_source = json_variant_is_regular(v) && v->source ? v->source->name : NULL;
if (ret_line)
- *ret_line = json_variant_is_magic(v) ? 0 : v->line;
+ *ret_line = json_variant_is_regular(v) ? v->line : 0;
if (ret_column)
- *ret_column = json_variant_is_magic(v) ? 0 : v->column;
+ *ret_column = json_variant_is_regular(v) ? v->column : 0;
return 0;
}
if (!FLAGS_SET(flags, JSON_FORMAT_SOURCE|JSON_FORMAT_PRETTY))
return 0;
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
return 0;
if (!v->source && v->line == 0 && v->column == 0)
/* Checks whether the caller is the single owner of the object, i.e. can get away with changing it */
- if (json_variant_is_magic(v))
+ if (!json_variant_is_regular(v))
return false;
if (v->is_embedded)
if (source && column > source->max_column)
source->max_column = column;
- if (json_variant_is_magic(*v)) {
+ if (!json_variant_is_regular(*v)) {
if (!source && line == 0 && column == 0)
return 0;
if (r < 0)
return r;
- assert(!json_variant_is_magic(w));
+ assert(json_variant_is_regular(w));
assert(!w->is_embedded);
assert(w->n_ref == 1);
assert(!w->source);