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.

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