+
+/*
+ * We store a bitfield in the hash table, with five possible values total.
+ *
+ * The ENABLED_BIT's purpose is self-explanatory.
+ *
+ * The FOUND_BIT is needed to differentiate the value of DISABLED from
+ * the value returned by HashTableLookup() when it can't find the given key.
+ *
+ * The KNOWN_SEVERITY bit is a bit complicated:
+ *
+ * A client may call Control() with an array of IDs, then call Control()
+ * on all message IDs of a certain severity, then Insert() one of the
+ * previously specified IDs, giving us a known severity level, then call
+ * Control() on all message IDs of a certain severity level again.
+ *
+ * After the first call, those IDs will have a FOUND_BIT, but will not
+ * exist in any severity-specific list, so the second call will not
+ * impact them. This is undesirable but unavoidable given the API:
+ * The only entrypoint that gives a severity for a client-defined ID
+ * is the Insert() call.
+ *
+ * For the sake of Control(), we want to maintain the invariant
+ * that an ID will either appear in none of the three severity lists,
+ * or appear once, to minimize pointless duplication and potential surprises.
+ *
+ * Because Insert() is the only place that will learn an ID's severity,
+ * it should insert an ID into the appropriate list, but only if the ID
+ * doesn't exist in it or any other list yet. Because searching all three
+ * lists at O(n) is needlessly expensive, we store KNOWN_SEVERITY.
+ */
+enum {
+ FOUND_BIT = 1 << 0,
+ ENABLED_BIT = 1 << 1,
+ KNOWN_SEVERITY = 1 << 2,
+
+ /* HashTable reserves zero as a return value meaning 'not found' */
+ NOT_FOUND = 0,
+ DISABLED = FOUND_BIT,
+ ENABLED = ENABLED_BIT | FOUND_BIT
+};
+
+/**
+ * Returns the state of the given message ID in a client-controlled
+ * namespace.
+ * 'source', 'type', and 'severity' are array indices like TYPE_ERROR,
+ * not GL enums.
+ */
+static GLboolean
+get_message_state(struct gl_context *ctx, int source, int type,
+ GLuint id, int severity)
+{
+ struct gl_client_namespace *nspace =
+ &ctx->Debug.ClientIDs.Namespaces[source][type];
+ uintptr_t state;
+
+ /* In addition to not being able to store zero as a value, HashTable also
+ can't use zero as a key. */
+ if (id)
+ state = (uintptr_t)_mesa_HashLookup(nspace->IDs, id);
+ else
+ state = nspace->ZeroID;
+
+ /* Only do this once for each ID. This makes sure the ID exists in,
+ at most, one list, and does not pointlessly appear multiple times. */
+ if (!(state & KNOWN_SEVERITY)) {
+ struct gl_client_severity *entry;
+
+ if (state == NOT_FOUND) {
+ if (ctx->Debug.ClientIDs.Defaults[severity][source][type])
+ state = ENABLED;
+ else
+ state = DISABLED;
+ }
+
+ entry = malloc(sizeof *entry);
+ if (!entry)
+ goto out;
+
+ state |= KNOWN_SEVERITY;
+
+ if (id)
+ _mesa_HashInsert(nspace->IDs, id, (void*)state);
+ else
+ nspace->ZeroID = state;
+
+ entry->ID = id;
+ insert_at_tail(&nspace->Severity[severity], &entry->link);
+ }
+
+out:
+ return !!(state & ENABLED_BIT);
+}
+
+/**
+ * Sets the state of the given message ID in a client-controlled
+ * namespace.
+ * 'source' and 'type' are array indices like TYPE_ERROR, not GL enums.
+ */
+static void
+set_message_state(struct gl_context *ctx, int source, int type,
+ GLuint id, GLboolean enabled)
+{
+ struct gl_client_namespace *nspace =
+ &ctx->Debug.ClientIDs.Namespaces[source][type];
+ uintptr_t state;
+
+ /* In addition to not being able to store zero as a value, HashTable also
+ can't use zero as a key. */
+ if (id)
+ state = (uintptr_t)_mesa_HashLookup(nspace->IDs, id);
+ else
+ state = nspace->ZeroID;
+
+ if (state == NOT_FOUND)
+ state = enabled ? ENABLED : DISABLED;
+ else {
+ if (enabled)
+ state |= ENABLED_BIT;
+ else
+ state &= ~ENABLED_BIT;
+ }
+
+ if (id)
+ _mesa_HashInsert(nspace->IDs, id, (void*)state);
+ else
+ nspace->ZeroID = state;
+}
+