+/*
+ * Balance JSON object and array parentheses, and separate array items.
+ * Somewhat convoluted API to re-use the routine for individual items as
+ * well as the surrounding array and object, including deferred start of
+ * the output and late flush (and to keep the state strictly local to the
+ * routine). Some additional complexity due to JSON's inability to handle
+ * a trailing comma at the last item. Code phrased such that text literals
+ * are kept in their order of appearance in the output (where possible).
+ */
+static void jsontrace_open_close(gboolean is_close_req,
+ gboolean open_item, gboolean close_item)
+{
+ static gboolean is_file_open;
+ static gboolean is_item_open;
+
+ if (is_close_req && is_item_open)
+ close_item = TRUE;
+
+ /* Automatic file header, and array item separation. */
+ if (open_item) {
+ if (!is_file_open)
+ printf("{\"traceEvents\": [\n");
+ if (is_item_open) {
+ printf("}");
+ is_item_open = FALSE;
+ }
+ if (is_file_open) {
+ printf(",\n");
+ }
+ is_file_open = TRUE;
+ }
+
+ /* Array item open/append/close. */
+ if (open_item) {
+ printf("{");
+ is_item_open = TRUE;
+ }
+ if (!open_item && !close_item && !is_close_req) {
+ printf(", ");
+ is_item_open = TRUE;
+ }
+ if (close_item) {
+ printf("}");
+ is_item_open = FALSE;
+ }
+
+ /* Automatic file footer on shutdown. */
+ if (is_close_req && is_file_open) {
+ printf("\n");
+ printf("]}\n");
+ }
+ if (is_close_req)
+ is_file_open = FALSE;
+
+ /* Flush at end of lines, or end of file. */
+ if (close_item || is_close_req)
+ fflush(stdout);
+}
+
+/* Convert uint64 sample number to double timestamp in microseconds. */
+static double jsontrace_ts_usec(uint64_t snum)
+{
+ double ts_usec;
+
+ ts_usec = snum;
+ ts_usec *= 1e6;
+ ts_usec /= pd_samplerate;
+ return ts_usec;
+}
+
+/* Emit two Google Trace Events (JSON) for one PD annotation (ss, es). */
+static void jsontrace_annotation(struct srd_decoder *dec,
+ struct srd_proto_data_annotation *pda, struct srd_proto_data *pdata)
+{
+ char *row_text;
+ GSList *lrow, *lcls;
+ struct srd_decoder_annotation_row *row;
+ int cls;
+ char **ann_descr;
+
+ /*
+ * Search for an annotation row for this index, or use the
+ * annotation's descriptor.
+ */
+ row_text = NULL;
+ if (dec->annotation_rows) {
+ for (lrow = dec->annotation_rows; lrow; lrow = lrow->next) {
+ row = lrow->data;
+ for (lcls = row->ann_classes; lcls; lcls = lcls->next) {
+ cls = GPOINTER_TO_INT(lcls->data);
+ if (cls == pda->ann_class) {
+ row_text = row->desc;
+ break;
+ }
+ }
+ if (row_text)
+ break;
+ }
+ }
+ if (!row_text) {
+ ann_descr = g_slist_nth_data(dec->annotations, pda->ann_class);
+ row_text = ann_descr[0];
+ }
+
+ /*
+ * Emit two Google Trace Events for the start and end times.
+ * Set the 'pid' (process ID) to the decoder name to group a
+ * decoder's annotations. Set the 'tid' (thread ID) to the
+ * annotation row's description. The 'ts' (timestamp) is in
+ * microseconds. Set 'name' to the longest annotation text.
+ *
+ * BEWARE of the unfortunate JSON format limitation, which
+ * clutters data output calls with format helper calls.
+ * TODO Want to introduce a cJSON dependency to delegate the
+ * construction of output text?
+ */
+ jsontrace_open_close(FALSE, TRUE, FALSE);
+ printf("\"%s\": \"%s\"", "ph", "B");
+ jsontrace_open_close(FALSE, FALSE, FALSE);
+ printf("\"%s\": %lf", "ts", jsontrace_ts_usec(pdata->start_sample));
+ jsontrace_open_close(FALSE, FALSE, FALSE);
+ printf("\"%s\": \"%s\"", "pid", pdata->pdo->proto_id);
+ jsontrace_open_close(FALSE, FALSE, FALSE);
+ printf("\"%s\": \"%s\"", "tid", row_text);
+ jsontrace_open_close(FALSE, FALSE, FALSE);
+ printf("\"%s\": \"%s\"", "name", pda->ann_text[0]);
+
+ jsontrace_open_close(FALSE, TRUE, FALSE);
+ printf("\"%s\": \"%s\"", "ph", "E");
+ jsontrace_open_close(FALSE, FALSE, FALSE);
+ printf("\"%s\": %lf", "ts", jsontrace_ts_usec(pdata->end_sample));
+ jsontrace_open_close(FALSE, FALSE, FALSE);
+ printf("\"%s\": \"%s\"", "pid", pdata->pdo->proto_id);
+ jsontrace_open_close(FALSE, FALSE, FALSE);
+ printf("\"%s\": \"%s\"", "tid", row_text);
+ jsontrace_open_close(FALSE, FALSE, FALSE);
+ printf("\"%s\": \"%s\"", "name", pda->ann_text[0]);
+
+ jsontrace_open_close(FALSE, FALSE, TRUE);
+}
+