Code

01854aa5779778f42e4bbdaf45bbad536a4e40d9
[sysdb.git] / src / core / timeseries.c
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)
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)
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)
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 : */