OSSIA
Open Scenario System for Interactive Application
value_to_json.hpp
1 #pragma once
2 #include <ossia/detail/fmt.hpp>
3 #include <ossia/detail/json.hpp>
4 #include <ossia/network/dataspace/color.hpp>
5 #include <ossia/network/dataspace/dataspace.hpp>
6 #include <ossia/network/value/value.hpp>
7 
8 #include <oscpack/osc/OscTypes.h>
9 namespace ossia::oscquery::detail
10 {
11 
12 // TODO base64 encode
13 struct value_to_json
14 {
15  ossia::json_writer& writer;
16  const ossia::unit_t& unit;
17  void operator()(impulse) const { writer.Null(); }
18  void operator()(int v) const { writer.Int(v); }
19  void operator()(float v) const { writer.Double(v); }
20  void operator()(bool v) const
21  {
22  writer.Null(); // the value is already encoded in the typetag
23  }
24  void operator()(char v) const { write_json(writer, v); }
25  void operator()(const std::string& v) const
26  {
27  // TODO handle base 64
28  // bool b = Base64::Encode(get<coppa::Generic>(val).buf, &out);
29  writer.String(v);
30  }
31 
32  template <std::size_t N>
33  void operator()(const std::array<float, N>& t) const
34  {
35  if constexpr(N == 4)
36  {
37  if(unit == ossia::rgba8_u{})
38  {
39  auto r = (uint8_t)t[0];
40  auto g = (uint8_t)t[1];
41  auto b = (uint8_t)t[2];
42  auto a = (uint8_t)t[3];
43 
44  writer.StartArray();
45  writer.String(fmt::format("#{:02X}{:02X}{:02X}{:02X}", r, g, b, a));
46  writer.EndArray();
47  return;
48  }
49  }
50 
51  writer.StartArray();
52  for(std::size_t i = 0; i < N; i++)
53  {
54  writer.Double(t[i]);
55  }
56  writer.EndArray();
57  }
58 
59  void operator()(const std::vector<ossia::value>& vec) const
60  {
61  writer.StartArray();
62  for(const auto& sub : vec)
63  {
64  sub.apply(*this);
65  }
66  writer.EndArray();
67  }
68 
69  void operator()(const value_map_type& vec) const { }
70  void operator()() const { throw std::runtime_error("value_to_json: no type"); }
71 };
72 
73 static inline auto from_hex(char c)
74 {
75  // taken from
76  // https://stackoverflow.com/questions/34365746/whats-the-fastest-way-to-convert-hex-to-integer-in-c
77  struct Table
78  {
79  long long tab[128];
80  constexpr Table()
81  : tab{}
82  {
83  tab[(int)'0'] = 0;
84  tab[(int)'1'] = 1;
85  tab[(int)'2'] = 2;
86  tab[(int)'3'] = 3;
87  tab[(int)'4'] = 4;
88  tab[(int)'5'] = 5;
89  tab[(int)'6'] = 6;
90  tab[(int)'7'] = 7;
91  tab[(int)'8'] = 8;
92  tab[(int)'9'] = 9;
93  tab[(int)'a'] = 10;
94  tab[(int)'A'] = 10;
95  tab[(int)'b'] = 11;
96  tab[(int)'B'] = 11;
97  tab[(int)'c'] = 12;
98  tab[(int)'C'] = 12;
99  tab[(int)'d'] = 13;
100  tab[(int)'D'] = 13;
101  tab[(int)'e'] = 14;
102  tab[(int)'E'] = 14;
103  tab[(int)'f'] = 15;
104  tab[(int)'F'] = 15;
105  }
106 
107  constexpr auto operator[](const std::size_t idx) const { return tab[idx]; }
108  };
109  static constexpr Table t;
110  return t[c];
111 }
112 
113 struct json_to_value
114 {
115  const rapidjson::Value& val;
116  std::string_view& typetags;
117  int& typetag_cursor;
118  const ossia::unit_t& unit;
119  bool operator()(impulse) const
120  {
121  typetag_cursor++;
122  return val.IsNull();
123  }
124 
125  bool operator()(int32_t& res) const
126  {
127  typetag_cursor++;
128 
129  bool b = val.IsInt();
130  if(b)
131  res = val.GetInt();
132  return b;
133  }
134 
135  bool operator()(float& res) const
136  {
137  typetag_cursor++;
138 
139  bool b = val.IsNumber();
140  if(b)
141  res = (float)val.GetDouble();
142  return b;
143  }
144 
145  bool operator()(bool& res) const
146  {
147  bool b = false;
148 
149  switch(typetags[typetag_cursor])
150  {
151  case 'F':
152  res = false;
153  b = true;
154  break;
155  case 'T':
156  res = true;
157  b = true;
158  break;
159  default:
160  b = val.IsBool();
161  if(b)
162  res = val.GetBool();
163  }
164  typetag_cursor++;
165  return b;
166  }
167 
168  bool operator()(char& res) const
169  {
170  typetag_cursor++;
171 
172  bool b = val.IsString() && val.GetStringLength() > 0;
173  if(b)
174  res = val.GetString()[0];
175  return b;
176  }
177 
178  bool operator()(std::string& res) const
179  {
180  typetag_cursor++;
181  // TODO handle base 64
182  // bool b = Base64::Encode(get<coppa::Generic>(val).buf, &out);
183 
184  bool b = val.IsString();
185  if(b)
186  res = std::string(val.GetString(), val.GetStringLength());
187  return b;
188  }
189 
190  template <std::size_t N>
191  bool operator()(std::array<float, N>& res) const
192  {
193  if constexpr(N == 4)
194  {
195  if(typetags[typetag_cursor] == oscpack::TypeTagValues::RGBA_COLOR_TYPE_TAG)
196  {
197  typetag_cursor += 1;
198  bool b = val.IsString();
199  if(b)
200  {
201  std::string_view hex(val.GetString(), val.GetStringLength());
202  if(hex.size() == 9) // "#00000000"
203  {
204  res[0] = (from_hex(hex[1]) * 16 + from_hex(hex[2]));
205  res[1] = (from_hex(hex[3]) * 16 + from_hex(hex[4]));
206  res[2] = (from_hex(hex[5]) * 16 + from_hex(hex[6]));
207  res[3] = (from_hex(hex[7]) * 16 + from_hex(hex[8]));
208  return true;
209  }
210  }
211  return false;
212  }
213  }
214 
215  typetag_cursor += N;
216  bool b = val.IsArray();
217  if(b)
218  {
219  auto arr = val.GetArray();
220  if(arr.Size() == N)
221  {
222  for(int i = 0; i < (int)N; i++)
223  {
224  if(arr[i].IsNumber())
225  {
226  res[i] = arr[i].GetDouble();
227  }
228  else
229  {
230  b = false;
231  break;
232  }
233  }
234  }
235  else
236  {
237  b = false;
238  }
239  }
240  return b;
241  }
242 
243  bool
244  handleVecElement(const rapidjson::Value& elt, std::vector<ossia::value>& res) const
245  {
246  if((int)typetags.size() > typetag_cursor)
247  {
248  switch(typetags[typetag_cursor])
249  {
250  case oscpack::TypeTagValues::INFINITUM_TYPE_TAG: {
251  ossia::impulse i;
252  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
253  return false;
254 
255  res.emplace_back(i);
256  return true;
257  }
258  case oscpack::TypeTagValues::INT32_TYPE_TAG: {
259  int32_t i{};
260  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
261  return false;
262 
263  res.emplace_back(i);
264  return true;
265  }
266  case oscpack::TypeTagValues::FLOAT_TYPE_TAG: {
267  float i{};
268  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
269  return false;
270 
271  res.emplace_back(i);
272  return true;
273  }
274  case oscpack::TypeTagValues::CHAR_TYPE_TAG: {
275  char i{};
276  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
277  return false;
278 
279  res.emplace_back(i);
280  return true;
281  }
282 
283  case oscpack::TypeTagValues::TRUE_TYPE_TAG:
284  case oscpack::TypeTagValues::FALSE_TYPE_TAG: {
285  bool i{};
286  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
287  return false;
288 
289  res.emplace_back(i);
290  return true;
291  }
292 
293  case oscpack::TypeTagValues::STRING_TYPE_TAG:
294  case oscpack::TypeTagValues::SYMBOL_TYPE_TAG: {
295  std::string i;
296  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
297  return false;
298 
299  res.emplace_back(std::move(i));
300  return true;
301  }
302 
303  case oscpack::TypeTagValues::ARRAY_BEGIN_TYPE_TAG: {
304  if(can_read("[ff]"))
305  {
306  ++typetag_cursor; // We skip the '['
307  std::array<float, 2> i;
308  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
309  return false;
310 
311  res.emplace_back(i);
312  ++typetag_cursor; // We skip the ']'
313  }
314  else if(can_read("[fff]"))
315  {
316  ++typetag_cursor; // We skip the '['
317  std::array<float, 3> i;
318  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
319  return false;
320 
321  res.emplace_back(i);
322  ++typetag_cursor; // We skip the ']'
323  }
324  else if(can_read("[ffff]"))
325  {
326  ++typetag_cursor; // We skip the '['
327  std::array<float, 4> i;
328  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
329  return false;
330 
331  res.emplace_back(i);
332  ++typetag_cursor; // We skip the ']'
333  }
334  else
335  {
336  std::vector<ossia::value> i;
337  ++typetag_cursor; // We skip the '['
338  if(!json_to_value{elt, typetags, typetag_cursor, unit}(i))
339  return false;
340 
341  ++typetag_cursor; // We skip the ']'
342  res.emplace_back(std::move(i));
343  }
344  return true;
345  }
346  case oscpack::TypeTagValues::ARRAY_END_TYPE_TAG:
347  default: {
348  // We should never end up here
349  return false;
350  }
351  }
352  }
353  else
354  {
355  return false;
356  }
357  }
358 
359  bool can_read(std::string_view sv) const noexcept
360  {
361  const auto res = typetags.find(sv);
362  if(res == std::string_view::npos)
363  {
364  return false;
365  }
366  else
367  {
368  return int64_t(res) == typetag_cursor;
369  }
370  }
371 
372  bool operator()(std::vector<ossia::value>& res) const
373  {
374  // TODO read from the typetag
375  bool b = val.IsArray();
376  if(b)
377  {
378  auto arr = val.GetArray();
379 
380  for(const auto& elt : arr)
381  {
382  if(!handleVecElement(elt, res))
383  return false;
384  }
385  }
386  return b;
387  }
388 
389  bool operator()(ossia::value_map_type& res) const { return false; }
390  bool operator()() const { throw std::runtime_error("json_to_value: no type"); }
391 };
392 
393 struct json_to_single_value
394 {
395  const rapidjson::Value& val;
396  std::string_view typetags;
397 
398  bool operator()(impulse) const { return val.IsNull(); }
399 
400  bool operator()(int32_t& res) const
401  {
402  bool b = val.IsInt();
403  if(b)
404  res = val.GetInt();
405  return b;
406  }
407 
408  bool operator()(float& res) const
409  {
410  bool b = val.IsNumber();
411  if(b)
412  res = (float)val.GetDouble();
413  return b;
414  }
415 
416  bool operator()(bool& res) const
417  {
418  bool b = false;
419  if(typetags == "F")
420  {
421  res = false;
422  b = true;
423  }
424  else if(typetags == "T")
425  {
426  res = true;
427  b = true;
428  }
429  else
430  {
431  // weird case where the typetag is wrongly set
432  // then we expect that the value is not null
433  // This doesn't follow the OSCQuery specification
434  bool b = val.IsBool();
435  if(b)
436  res = val.GetBool();
437  }
438  return b;
439  }
440 
441  bool operator()(char& res) const
442  {
443  bool b = val.IsString() && val.GetStringLength() > 0;
444  if(b)
445  res = val.GetString()[0];
446  return b;
447  }
448 
449  bool operator()(std::string& res) const
450  {
451  // TODO handle base 64
452  // bool b = Base64::Encode(get<coppa::Generic>(val).buf, &out);
453 
454  bool b = val.IsString();
455  if(b)
456  res = std::string(val.GetString(), val.GetStringLength());
457  return b;
458  }
459 
460  template <std::size_t N>
461  bool operator()(std::array<float, N>& res) const
462  {
463  if constexpr(N == 4)
464  {
465  if(typetags[0] == oscpack::TypeTagValues::RGBA_COLOR_TYPE_TAG)
466  {
467  bool b = val.IsString();
468  if(b)
469  {
470  std::string_view hex(val.GetString(), val.GetStringLength());
471  if(hex.size() == 9) // "#00000000"
472  {
473  res[0] = (from_hex(hex[1]) * 16 + from_hex(hex[2]));
474  res[1] = (from_hex(hex[3]) * 16 + from_hex(hex[4]));
475  res[2] = (from_hex(hex[5]) * 16 + from_hex(hex[6]));
476  res[3] = (from_hex(hex[7]) * 16 + from_hex(hex[8]));
477  return true;
478  }
479  }
480  return false;
481  }
482  }
483 
484  return false;
485  }
486 
487  bool operator()(std::vector<ossia::value>& res) const { return false; }
488  bool operator()(ossia::value_map_type& res) const { return false; }
489 
490  bool operator()() const { throw std::runtime_error("json_to_value: no type"); }
491 };
492 
493 inline ossia::value ReadValue(const rapidjson::Value& val)
494 {
495  switch(val.GetType())
496  {
497  case rapidjson::kNumberType: {
498  if(val.IsInt())
499  return val.GetInt();
500  else if(val.IsUint())
501  return (int)val.GetUint();
502  // There is also int64 and uint64 but we'll get a better approximation
503  // with double
504  else
505  return val.GetDouble();
506  }
507  case rapidjson::kFalseType:
508  return false;
509  case rapidjson::kTrueType:
510  return true;
511 
512  case rapidjson::kArrayType: {
513  std::vector<ossia::value> tpl;
514  tpl.reserve(val.Size());
515  for(auto& elt : val.GetArray())
516  {
517  tpl.push_back(ReadValue(elt));
518  }
519  return ossia::value{std::move(tpl)};
520  }
521 
522  case rapidjson::kStringType:
523  return get_string(val);
524 
525  case rapidjson::kObjectType:
526  case rapidjson::kNullType:
527  default:
528  return ossia::impulse{};
529  }
530 }
531 
532 struct json_to_value_unchecked
533 {
534  const rapidjson::Value& val;
535  void operator()(impulse) const { }
536 
537  void operator()(int32_t& res) const
538  {
539  if(val.IsInt())
540  res = val.GetInt();
541  }
542 
543  void operator()(float& res) const
544  {
545  if(val.IsNumber())
546  res = (float)val.GetDouble();
547  }
548 
549  void operator()(bool& res) const
550  {
551  if(val.IsBool())
552  res = val.GetBool();
553  }
554 
555  void operator()(char& res) const
556  {
557  if(val.IsString() && val.GetStringLength() > 0)
558  res = val.GetString()[0];
559  }
560 
561  void operator()(std::string& res) const
562  {
563  // TODO handle base 64
564  // bool b = Base64::Encode(get<coppa::Generic>(val).buf, &out);
565 
566  if(val.IsString())
567  res = get_string(val);
568  }
569 
570  template <std::size_t N>
571  void operator()(std::array<float, N>& res) const
572  {
573  if(val.IsArray())
574  {
575  auto arr = val.GetArray();
576  if(arr.Size() == N)
577  {
578  for(int i = 0; i < (int)N; i++)
579  {
580  res[i] = arr[i].GetDouble();
581  }
582  }
583  }
584  }
585 
586  void operator()(std::vector<ossia::value>& res) const
587  {
588  if(val.IsArray())
589  {
590  res.clear();
591  auto arr = val.GetArray();
592  for(const auto& elt : arr)
593  {
594  res.push_back(ReadValue(elt));
595  }
596  }
597  }
598 
599  void operator()(value_map_type& res) const { }
600 
601  void operator()() const
602  {
603  throw std::runtime_error("json_to_value_unchecked: no type");
604  }
605 };
606 }
The value class.
Definition: value.hpp:173
Definition: dataspace.hpp:24