General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
13 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. #pragma once
  2. #include <gp/algorithms/repeat.hpp>
  3. #include <gp/math/boolean/bitops.hpp>
  4. #include <gp/functional/optional.hpp>
  5. #include <gp/utils/pair.hpp>
  6. #include <gp/functional/variant.hpp>
  7. #include <gp/containers/vector.hpp>
  8. #include <concepts>
  9. // TODO: Rewrite in a more ORM-like way
  10. // TODO: Implement some bignum for support
  11. namespace gp {
  12. enum class cbor_type : uint8_t {
  13. uint = 0,
  14. nint = 1,
  15. bstr = 2,
  16. tstr = 3,
  17. list = 4,
  18. hmap = 5,
  19. tags = 6,
  20. oths = 7
  21. };
  22. enum class cbor_oths : uint8_t {
  23. value_false = 20,
  24. value_true = 21,
  25. value_null = 22,
  26. value_undefined = 23,
  27. byte = 24,
  28. word = 25,
  29. dword = 26,
  30. qword = 27,
  31. terminator = 31
  32. };
  33. struct cbor_number final {
  34. bool sign;
  35. uint64_t value;
  36. bool is_negative() {
  37. return sign;
  38. }
  39. cbor_number(int64_t v)
  40. : sign{v < 0}
  41. , value{uint64_t((sign ? -1 : 1) * v)}
  42. {}
  43. cbor_number(bool s, uint64_t v)
  44. : sign{s}
  45. , value{v}
  46. {}
  47. };
  48. struct ieee754_hf final {
  49. uint16_t sign : 1;
  50. uint16_t exponent : 5;
  51. uint16_t mantissa : 10;
  52. // TODO: support for denormalized values and NaNs
  53. operator float() {
  54. auto a = (uint32_t)((sign<<16) | ((exponent+0x1C000)<<13) | (mantissa<<13));
  55. return *(float*)&a;
  56. }
  57. operator double() {
  58. return (float)*this;
  59. }
  60. };
  61. inline vector<char>& push_integer_with_header_as_cbor(vector<char>& src, uint8_t header, uint64_t value) {
  62. auto norm_v = (value<0) ? -value : value;
  63. if(norm_v <= 23) {
  64. src.push_back(header+norm_v);
  65. } else if(norm_v < (1ll<<8ll)) {
  66. src.push_back(header+24);
  67. src.push_back(norm_v);
  68. } else if(norm_v < (1ll<<16ll)) {
  69. endian_wrapper<uint16_t, endian::big> wrapper = norm_v;
  70. src.push_back(header+25);
  71. for(auto byte : wrapper.bytes()) {
  72. src.push_back(byte);
  73. }
  74. } else if(norm_v < (1ll<<32ll)) {
  75. endian_wrapper<uint32_t, endian::big> wrapper = norm_v;
  76. src.push_back(header+26);
  77. for(auto byte : wrapper.bytes()) {
  78. src.push_back(byte);
  79. }
  80. } else {
  81. endian_wrapper<uint64_t, endian::big> wrapper = norm_v;
  82. src.push_back(header+27);
  83. for(auto byte : wrapper.bytes()) {
  84. src.push_back(byte);
  85. }
  86. }
  87. return src;
  88. }
  89. /**
  90. * @brief Pushes an integer as CBOR on the vector
  91. *
  92. * @param src the vector on which the push happens
  93. * @param value the value to push, can be signed
  94. * @return vector<char>& the same reference that was received for the source
  95. */
  96. template<std::signed_integral T>
  97. inline vector<char>& push_as_cbor(vector<char>& src, T value) {
  98. uint8_t sign = (value<0) ? 0b00100000 : 0;
  99. auto norm_v = (value<0) ? -value : value;
  100. return push_integer_with_header_as_cbor(src, sign, norm_v);
  101. }
  102. /**
  103. * @brief Pushes an unsigned integer as CBOR on the vector
  104. *
  105. * @param src the vector on which the push happens
  106. * @param value the value to push, cannot be signed
  107. * @return vector<char>& the same reference that was received for the source
  108. */
  109. template<std::unsigned_integral T>
  110. inline vector<char>& push_as_cbor(vector<char>& src, T value) {
  111. return push_integer_with_header_as_cbor(src, 0, value);
  112. }
  113. inline vector<char>& push_as_cbor(vector<char>& src, std::nullptr_t) {
  114. src.push_back(0b11110110);
  115. return src;
  116. }
  117. struct cbor_undefined{};
  118. inline vector<char>& push_as_cbor(vector<char>& src, cbor_undefined) {
  119. src.push_back(0b11110111);
  120. return src;
  121. }
  122. inline vector<char>& push_as_cbor(vector<char>& src, bool value) {
  123. src.push_back(0b11110100+(value ? 1 : 0));
  124. return src;
  125. }
  126. inline vector<char>& push_as_cbor(vector<char>& src, gp::buffer<char> value) {
  127. push_integer_with_header_as_cbor(src, (uint8_t)0b01000000, value.size());
  128. for(auto byte : value) {
  129. src.push_back(byte);
  130. }
  131. return src;
  132. }
  133. struct cbor_array_initiator {
  134. size_t size;
  135. };
  136. struct cbor_associative_array_initiator {
  137. size_t size;
  138. };
  139. inline vector<char>& push_as_cbor(vector<char>& src, cbor_array_initiator value) {
  140. return push_integer_with_header_as_cbor(src, (uint8_t)0b10000000, value.size);
  141. }
  142. inline vector<char>& push_as_cbor(vector<char>& src, cbor_associative_array_initiator value) {
  143. return push_integer_with_header_as_cbor(src, (uint8_t)0b10100000, value.size);
  144. }
  145. template<typename First, typename Second>
  146. inline vector<char>& push_as_cbor(vector<char>& src, gp::pair<First, Second> value) {
  147. push_as_cbor(src,value.first);
  148. return push_as_cbor(src,value.second);
  149. }
  150. template<typename First, typename Second>
  151. inline vector<char>& push_as_cbor(vector<char>& src, gp::pair<First, Second>& value) {
  152. push_as_cbor(src,value.first);
  153. return push_as_cbor(src,value.second);
  154. }
  155. struct cbor_tag_initiator {
  156. union {
  157. size_t as_integer;
  158. gp_config::cbor_tag tag;
  159. };
  160. };
  161. inline vector<char>& push_as_cbor(vector<char>& src, cbor_tag_initiator value) {
  162. return push_integer_with_header_as_cbor(src, (uint8_t)0b11000000, value.as_integer);
  163. }
  164. using parsing_state = gp::buffer<char>;
  165. template<typename T>
  166. gp::pair<gp::optional<T>, parsing_state> read_cbor(parsing_state state, gp::allocator&);
  167. inline gp::pair<gp::optional<uint64_t>, parsing_state> pull_arbitrary_integer_from_cbor(parsing_state state) {
  168. auto local = (uint8_t)0b00011111 & (uint8_t)*state.begin();
  169. if(local <= 23) {
  170. return {local, {state.begin()+1, state.end()}};
  171. } else {
  172. switch((cbor_oths)local) {
  173. case cbor_oths::byte: {
  174. if(state.size() < 2) return {nullopt, state};
  175. return {*(state.begin()+1), {state.begin()+2, state.end()}};
  176. }
  177. case cbor_oths::word: {
  178. if(state.size() < 3) return {nullopt, state};
  179. return {
  180. uint16_t(*(state.slice_start(3).slice_end(2).cast<gp::endian_wrapper<uint16_t, endian::big>>().begin())),
  181. {state.begin()+3, state.end()}
  182. };
  183. }
  184. case cbor_oths::dword: {
  185. if(state.size() < 5) return {nullopt, state};
  186. return {
  187. uint32_t(*(state.slice_start(5).slice_end(4).cast<gp::endian_wrapper<uint32_t, endian::big>>().begin())),
  188. {state.begin()+5, state.end()}
  189. };
  190. }
  191. case cbor_oths::qword: {
  192. if(state.size() < 9) return {nullopt, state};
  193. return {
  194. uint64_t(*(state.slice_start(9).slice_end(8).cast<gp::endian_wrapper<uint64_t, endian::big>>().begin())),
  195. {state.begin()+9, state.end()}
  196. };
  197. }
  198. default: {
  199. return {nullopt, state};
  200. }
  201. }
  202. }
  203. }
  204. template<std::integral T>
  205. inline gp::pair<gp::optional<T>, parsing_state> read_cbor(parsing_state state, gp::allocator&) {
  206. // TODO: Handling of over and underflow
  207. if(!state.size()) return {nullopt, state};
  208. auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
  209. switch(type) {
  210. case cbor_type::uint:
  211. {
  212. auto[value, new_state] = pull_arbitrary_integer_from_cbor(state);
  213. if(value.has_value()) return {value.value(), new_state};
  214. break;
  215. }
  216. case cbor_type::nint:
  217. {
  218. auto[value, new_state] = pull_arbitrary_integer_from_cbor(state);
  219. if(value.has_value()) return {-value.value(), new_state};
  220. break;
  221. }
  222. default:
  223. break;
  224. }
  225. return {nullopt, state};
  226. }
  227. template<>
  228. inline gp::pair<gp::optional<cbor_tag_initiator>, parsing_state> read_cbor<cbor_tag_initiator>(parsing_state state, gp::allocator&) {
  229. if(!state.size()) return {nullopt, state};
  230. auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
  231. switch(type) {
  232. case cbor_type::tags:
  233. {
  234. auto[value, new_state] = pull_arbitrary_integer_from_cbor(state);
  235. if(value.has_value()) return {cbor_tag_initiator{.as_integer = value.value()}, new_state};
  236. break;
  237. }
  238. default: break;
  239. }
  240. return {nullopt, state};
  241. }
  242. template<>
  243. inline gp::pair<gp::optional<gp::vector<char>>, parsing_state> read_cbor<gp::vector<char>>(parsing_state state, gp::allocator& alloc) {
  244. if(!state.size()) return {nullopt, state};
  245. auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
  246. switch(type) {
  247. case cbor_type::bstr:
  248. {
  249. const auto[size, new_state] = pull_arbitrary_integer_from_cbor(state);
  250. if(!size.has_value()) break;
  251. if(new_state.size()<size.value()) break;
  252. gp::vector<char> return_value{alloc};
  253. if(!return_value.reserve(size.value())) break;
  254. auto end_it = new_state.begin() + size.value();
  255. for(auto it = new_state.begin(); it != end_it; it++) {
  256. return_value.push_back(*it);
  257. }
  258. return {optional<gp::vector<char>>(gp::move(return_value)), parsing_state(new_state.begin() + size.value(), new_state.end())};
  259. break;
  260. }
  261. default: break;
  262. }
  263. return {nullopt, state};
  264. }
  265. template<>
  266. inline gp::pair<gp::optional<std::nullptr_t>, parsing_state> read_cbor<std::nullptr_t>(parsing_state state, gp::allocator& alloc) {
  267. if(!state.size()) return {nullopt, state};
  268. auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
  269. switch(type) {
  270. case cbor_type::oths:
  271. {
  272. const auto[value, new_state] = pull_arbitrary_integer_from_cbor(state);
  273. if(!value.has_value()) break;
  274. if(value.value() == 22)
  275. {
  276. return {optional(nullptr), parsing_state(new_state.begin()+1, new_state.end())};
  277. }
  278. break;
  279. }
  280. default: break;
  281. }
  282. return {nullopt, state};
  283. }
  284. template<>
  285. inline gp::pair<gp::optional<bool>, parsing_state> read_cbor<bool>(parsing_state state, gp::allocator& alloc) {
  286. if(!state.size()) return {nullopt, state};
  287. auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
  288. switch(type) {
  289. case cbor_type::oths:
  290. {
  291. const auto[value, new_state] = pull_arbitrary_integer_from_cbor(state);
  292. if(!value.has_value()) break;
  293. if(value.value() == 20)
  294. {
  295. return {false, parsing_state(new_state.begin()+1, new_state.end())};
  296. }
  297. else if(value.value() == 21)
  298. {
  299. return {true, parsing_state(new_state.begin()+1, new_state.end())};
  300. }
  301. break;
  302. }
  303. default: break;
  304. }
  305. return {nullopt, state};
  306. }
  307. template<>
  308. inline gp::pair<gp::optional<cbor_undefined>, parsing_state> read_cbor<cbor_undefined>(parsing_state state, gp::allocator& alloc) {
  309. if(!state.size()) return {nullopt, state};
  310. auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
  311. switch(type) {
  312. case cbor_type::oths:
  313. {
  314. const auto[value, new_state] = pull_arbitrary_integer_from_cbor(state);
  315. if(!value.has_value()) break;
  316. if(value.value() == 23)
  317. {
  318. return {optional(cbor_undefined{}), parsing_state(new_state.begin()+1, new_state.end())};
  319. }
  320. break;
  321. }
  322. default: break;
  323. }
  324. return {nullopt, state};
  325. }
  326. /**
  327. * @brief
  328. *
  329. * @param state
  330. * @param callback a callback that returns a new parsing state for every element read. It should follow the heredescribed signature: parsing_state(parsing_state, gp::allocator&)
  331. * @param count_callback a callback that is used to check if the process should proceed given a count of elements in the list. It should follow the heredescribed signature: bool(uint64_t)
  332. * @param alloc
  333. */
  334. template<typename applier_cb, typename counter_cb>
  335. inline parsing_state read_cbor_array(parsing_state state, gp::allocator& alloc, applier_cb callback, counter_cb count_callback = [](uint64_t) -> bool {return true;}) {
  336. if(!state.size()) return state;
  337. auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
  338. switch(type) {
  339. case cbor_type::list:
  340. {
  341. const auto[size, new_state] = pull_arbitrary_integer_from_cbor(state);
  342. if(!size.has_value()) return state;
  343. if(new_state.size()<size.value()) return state;
  344. if(!count_callback(size.value()))return state;
  345. parsing_state forward = new_state;
  346. for(auto idx = 0ull; idx != size.value(); idx++) {
  347. if(forward.size() == 0) return state;
  348. forward = callback(forward, alloc);
  349. }
  350. return forward;
  351. }
  352. default: return state;
  353. }
  354. }
  355. /**
  356. * @brief
  357. *
  358. * @param state
  359. * @param callback a callback that returns a new parsing state for every element read. It should follow the heredescribed signature: parsing_state(parsing_state, gp::allocator&), it MUST read exactly 2 cbor value from the stream.
  360. * @param count_callback a callback that is used to check if the process should proceed given a count of elements in the list. It should follow the heredescribed signature: bool(uint64_t)
  361. * @param alloc
  362. */
  363. template<typename applier_cb, typename counter_cb>
  364. inline parsing_state read_cbor_kv_list(parsing_state state, gp::allocator& alloc, applier_cb callback, counter_cb count_callback = [](uint64_t) -> bool {return true;}) {
  365. if(!state.size()) return state;
  366. auto type = cbor_type(((uint8_t)0b11100000 & (uint8_t)*state.begin()) >> 5);
  367. switch(type) {
  368. case cbor_type::hmap:
  369. {
  370. const auto[size, new_state] = pull_arbitrary_integer_from_cbor(state);
  371. if(!size.has_value()) return state;
  372. if(!count_callback(size.value()))return state;
  373. parsing_state forward = new_state;
  374. for(auto idx = 0ull; idx != size.value(); idx++) {
  375. if(forward.size() < 2ull) return state;
  376. forward = callback(forward, alloc);
  377. forward = callback(forward, alloc);
  378. }
  379. return forward;
  380. }
  381. default: return state;
  382. }
  383. }
  384. }