1 /*
2 * SysDB - src/core/timeseries.c
3 * Copyright (C) 2014-2016 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 #if HAVE_CONFIG_H
29 # include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "sysdb.h"
33 #include "core/timeseries.h"
35 #include <stdlib.h>
36 #include <string.h>
37 #include <math.h>
39 static int
40 copy_strings(char ***out, size_t *out_len,
41 const char * const *in, size_t in_len)
42 {
43 size_t i;
45 *out = calloc(in_len, sizeof(**out));
46 if (! *out)
47 return -1;
49 *out_len = in_len;
50 for (i = 0; i < in_len; ++i) {
51 (*out)[i] = strdup(in[i]);
52 if (! (*out)[i])
53 return -1;
54 }
55 return 0;
56 } /* copy_strings */
58 static void
59 free_strings(char ***strings, size_t *strings_len)
60 {
61 size_t i;
63 if (*strings) {
64 for (i = 0; i < *strings_len; ++i) {
65 if ((*strings)[i])
66 free((*strings)[i]);
67 (*strings)[i] = NULL;
68 }
69 free(*strings);
70 }
72 *strings = NULL;
73 *strings_len = 0;
74 } /* free_strings */
76 /*
77 * public API
78 */
80 sdb_timeseries_info_t *
81 sdb_timeseries_info_create(size_t data_names_len, const char * const *data_names)
82 {
83 sdb_timeseries_info_t *ts_info;
85 ts_info = calloc(1, sizeof(*ts_info));
86 if (! ts_info)
87 return NULL;
89 if (copy_strings(&ts_info->data_names, &ts_info->data_names_len,
90 data_names, data_names_len)) {
91 sdb_timeseries_info_destroy(ts_info);
92 return NULL;
93 }
94 return ts_info;
95 } /* sdb_timeseries_info_create */
97 void
98 sdb_timeseries_info_destroy(sdb_timeseries_info_t *ts_info)
99 {
100 if (! ts_info)
101 return;
103 free_strings(&ts_info->data_names, &ts_info->data_names_len);
104 free(ts_info);
105 } /* sdb_timeseries_info_destroy */
107 sdb_timeseries_t *
108 sdb_timeseries_create(size_t data_names_len, const char * const *data_names,
109 size_t data_len)
110 {
111 sdb_timeseries_t *ts;
112 size_t i;
114 ts = calloc(1, sizeof(*ts));
115 if (! ts)
116 return NULL;
118 if (copy_strings(&ts->data_names, &ts->data_names_len,
119 data_names, data_names_len)) {
120 sdb_timeseries_destroy(ts);
121 return NULL;
122 }
124 ts->data = calloc(data_names_len, sizeof(*ts->data));
125 if (! ts->data) {
126 sdb_timeseries_destroy(ts);
127 return NULL;
128 }
129 for (i = 0; i < data_names_len; ++i) {
130 ts->data[i] = calloc(data_len, sizeof(**ts->data));
131 if (! ts->data[i]) {
132 sdb_timeseries_destroy(ts);
133 return NULL;
134 }
135 }
136 ts->data_len = data_len;
137 return ts;
138 } /* sdb_timeseries_create */
140 void
141 sdb_timeseries_destroy(sdb_timeseries_t *ts)
142 {
143 size_t i;
145 if (! ts)
146 return;
148 if (ts->data) {
149 for (i = 0; i < ts->data_names_len; ++i) {
150 if (ts->data[i])
151 free(ts->data[i]);
152 ts->data[i] = NULL;
153 }
154 free(ts->data);
155 }
156 ts->data = NULL;
157 ts->data_len = 0;
159 free_strings(&ts->data_names, &ts->data_names_len);
160 free(ts);
161 } /* sdb_timeseries_destroy */
163 int
164 sdb_timeseries_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
165 {
166 char start_str[64];
167 char end_str[64];
169 size_t i;
171 if ((! ts) || (! buf))
172 return -1;
174 /* TODO: make time format configurable */
175 if (! sdb_strftime(start_str, sizeof(start_str), ts->start))
176 snprintf(start_str, sizeof(start_str), "<error>");
177 start_str[sizeof(start_str) - 1] = '\0';
178 if (! sdb_strftime(end_str, sizeof(end_str), ts->end))
179 snprintf(end_str, sizeof(end_str), "<error>");
180 end_str[sizeof(end_str) - 1] = '\0';
182 sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
183 start_str, end_str);
185 for (i = 0; i < ts->data_names_len; ++i) {
186 size_t j;
187 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
189 for (j = 0; j < ts->data_len; ++j) {
190 char time_str[64];
192 if (! sdb_strftime(time_str, sizeof(time_str), ts->data[i][j].timestamp))
193 snprintf(time_str, sizeof(time_str), "<error>");
194 time_str[sizeof(time_str) - 1] = '\0';
196 /* Some GNU libc versions may print '-nan' which we dont' want */
197 if (isnan(ts->data[i][j].value))
198 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
199 "\"value\": \"nan\"}", time_str);
200 else
201 sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
202 "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
204 if (j < ts->data_len - 1)
205 sdb_strbuf_append(buf, ",");
206 }
208 if (i < ts->data_names_len - 1)
209 sdb_strbuf_append(buf, "],");
210 else
211 sdb_strbuf_append(buf, "]");
212 }
213 sdb_strbuf_append(buf, "}}");
214 return 0;
215 } /* sdb_timeseries_tojson */
217 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */