base32.hpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /**
  2. * Copyright (C) 2015 Trustifier Inc.
  3. * Copyright (C) 2015 Ahmed Masud
  4. * Copyright (C) 2015 Topology LP
  5. * Copyright (C) 2018 Jakob Petsovits
  6. * All rights reserved.
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to
  10. * deal in the Software without restriction, including without limitation the
  11. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  12. * sell copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  23. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  24. * IN THE SOFTWARE.
  25. *
  26. * Adapted from https://github.com/ahmed-masud/libbase32,
  27. * commit 79761b2b79b0545697945efe0987a8d3004512f9.
  28. * Quite different now.
  29. */
  30. #ifndef CPPCODEC_DETAIL_BASE32
  31. #define CPPCODEC_DETAIL_BASE32
  32. #include <stdint.h>
  33. #include <stdlib.h> // for abort()
  34. #include "../data/access.hpp"
  35. #include "../parse_error.hpp"
  36. #include "config.hpp"
  37. #include "stream_codec.hpp"
  38. namespace cppcodec {
  39. namespace detail {
  40. template <typename CodecVariant>
  41. class base32 : public CodecVariant::template codec_impl<base32<CodecVariant>>
  42. {
  43. public:
  44. static inline constexpr uint8_t binary_block_size() { return 5; }
  45. static inline constexpr uint8_t encoded_block_size() { return 8; }
  46. static CPPCODEC_ALWAYS_INLINE constexpr uint8_t num_encoded_tail_symbols(uint8_t num_bytes)
  47. {
  48. return (num_bytes == 1) ? 2 // 2 symbols, 6 padding characters
  49. : (num_bytes == 2) ? 4 // 4 symbols, 4 padding characters
  50. : (num_bytes == 3) ? 5 // 5 symbols, 3 padding characters
  51. : (num_bytes == 4) ? 7 // 7 symbols, 1 padding characters
  52. : throw std::domain_error("invalid number of bytes in a tail block");
  53. }
  54. template <uint8_t I>
  55. static CPPCODEC_ALWAYS_INLINE constexpr uint8_t index(
  56. const uint8_t* b /*binary block*/) noexcept
  57. {
  58. static_assert(I >= 0 && I < encoded_block_size(),
  59. "invalid encoding symbol index in a block");
  60. return (I == 0) ? ((b[0] >> 3) & 0x1F) // first 5 bits
  61. : (I == 1) ? (((b[0] << 2) & 0x1C) | ((b[1] >> 6) & 0x3))
  62. : (I == 2) ? ((b[1] >> 1) & 0x1F)
  63. : (I == 3) ? (((b[1] << 4) & 0x10) | ((b[2] >> 4) & 0xF))
  64. : (I == 4) ? (((b[2] << 1) & 0x1E) | ((b[3] >> 7) & 0x1))
  65. : (I == 5) ? ((b[3] >> 2) & 0x1F)
  66. : (I == 6) ? (((b[3] << 3) & 0x18) | ((b[4] >> 5) & 0x7))
  67. : /*I == 7*/ (b[4] & 0x1F); // last 5 bits;
  68. }
  69. template <bool B>
  70. using uint8_if = typename std::enable_if<B, uint8_t>::type;
  71. template <uint8_t I>
  72. static CPPCODEC_ALWAYS_INLINE constexpr
  73. uint8_if<I == 1 || I == 3 || I == 4 || I == 6> index_last(
  74. const uint8_t* b /*binary block*/) noexcept
  75. {
  76. return (I == 1) ? ((b[0] << 2) & 0x1C) // abbreviated 2nd symbol
  77. : (I == 3) ? ((b[1] << 4) & 0x10) // abbreviated 4th symbol
  78. : (I == 4) ? ((b[2] << 1) & 0x1E) // abbreviated 5th symbol
  79. : /*I == 6*/ ((b[3] << 3) & 0x18); // abbreviated 7th symbol
  80. }
  81. template <uint8_t I>
  82. static CPPCODEC_ALWAYS_INLINE
  83. uint8_if<I != 1 && I != 3 && I != 4 && I != 6> index_last(
  84. const uint8_t* /*binary block*/)
  85. {
  86. throw std::domain_error("invalid last encoding symbol index in a tail");
  87. }
  88. template <typename Result, typename ResultState>
  89. static CPPCODEC_ALWAYS_INLINE void decode_block(
  90. Result& decoded, ResultState&, const alphabet_index_t* idx);
  91. template <typename Result, typename ResultState>
  92. static CPPCODEC_ALWAYS_INLINE void decode_tail(
  93. Result& decoded, ResultState&, const alphabet_index_t* idx, size_t idx_len);
  94. };
  95. //
  96. // 11111111 10101010 10110011 10111100 10010100
  97. // => 11111 11110 10101 01011 00111 01111 00100 10100
  98. //
  99. template <typename CodecVariant>
  100. template <typename Result, typename ResultState>
  101. CPPCODEC_ALWAYS_INLINE void base32<CodecVariant>::decode_block(
  102. Result& decoded, ResultState& state, const alphabet_index_t* idx)
  103. {
  104. put(decoded, state, static_cast<uint8_t>(((idx[0] << 3) & 0xF8) | ((idx[1] >> 2) & 0x7)));
  105. put(decoded, state, static_cast<uint8_t>(((idx[1] << 6) & 0xC0) | ((idx[2] << 1) & 0x3E) | ((idx[3] >> 4) & 0x1)));
  106. put(decoded, state, static_cast<uint8_t>(((idx[3] << 4) & 0xF0) | ((idx[4] >> 1) & 0xF)));
  107. put(decoded, state, static_cast<uint8_t>(((idx[4] << 7) & 0x80) | ((idx[5] << 2) & 0x7C) | ((idx[6] >> 3) & 0x3)));
  108. put(decoded, state, static_cast<uint8_t>(((idx[6] << 5) & 0xE0) | (idx[7] & 0x1F)));
  109. }
  110. template <typename CodecVariant>
  111. template <typename Result, typename ResultState>
  112. CPPCODEC_ALWAYS_INLINE void base32<CodecVariant>::decode_tail(
  113. Result& decoded, ResultState& state, const alphabet_index_t* idx, size_t idx_len)
  114. {
  115. if (idx_len == 1) {
  116. throw invalid_input_length(
  117. "invalid number of symbols in last base32 block: found 1, expected 2, 4, 5 or 7");
  118. }
  119. if (idx_len == 3) {
  120. throw invalid_input_length(
  121. "invalid number of symbols in last base32 block: found 3, expected 2, 4, 5 or 7");
  122. }
  123. if (idx_len == 6) {
  124. throw invalid_input_length(
  125. "invalid number of symbols in last base32 block: found 6, expected 2, 4, 5 or 7");
  126. }
  127. // idx_len == 2: decoded size 1
  128. put(decoded, state, static_cast<uint8_t>(((idx[0] << 3) & 0xF8) | ((idx[1] >> 2) & 0x7)));
  129. if (idx_len == 2) {
  130. return;
  131. }
  132. // idx_len == 4: decoded size 2
  133. put(decoded, state, static_cast<uint8_t>(((idx[1] << 6) & 0xC0) | ((idx[2] << 1) & 0x3E) | ((idx[3] >> 4) & 0x1)));
  134. if (idx_len == 4) {
  135. return;
  136. }
  137. // idx_len == 5: decoded size 3
  138. put(decoded, state, static_cast<uint8_t>(((idx[3] << 4) & 0xF0) | ((idx[4] >> 1) & 0xF)));
  139. if (idx_len == 5) {
  140. return;
  141. }
  142. // idx_len == 7: decoded size 4
  143. put(decoded, state, static_cast<uint8_t>(((idx[4] << 7) & 0x80) | ((idx[5] << 2) & 0x7C) | ((idx[6] >> 3) & 0x3)));
  144. }
  145. } // namespace detail
  146. } // namespace cppcodec
  147. #endif // CPPCODEC_DETAIL_BASE32