base64.h 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Base64 encoder/decoder
  2. //
  3. // Platform: ISO C++ 98/11
  4. // $Id$
  5. //
  6. // (c) __vic 2008
  7. #ifndef __VIC_BASE64_H
  8. #define __VIC_BASE64_H
  9. #include<__vic/defs.h>
  10. #include<exception>
  11. #include<cstring>
  12. namespace __vic {
  13. //////////////////////////////////////////////////////////////////////////////
  14. struct base64
  15. {
  16. __VIC_SCOPED_ENUM_BEGIN(status)
  17. {
  18. ok,
  19. invalid_length,
  20. invalid_digit
  21. }
  22. __VIC_SCOPED_ENUM_END(status)
  23. struct bad_format : public std::exception
  24. {
  25. };
  26. struct bad_length : public bad_format
  27. {
  28. const char *what() const noexcept;
  29. };
  30. struct bad_digit : public bad_format
  31. {
  32. const char *what() const noexcept;
  33. };
  34. static const char abc[64]; // BASE64 alphabet
  35. // Bytes -> Text
  36. template<class ByteReader, class CharWriter>
  37. static void encode(ByteReader , CharWriter );
  38. // Text -> Bytes
  39. template<class CharReader, class ByteWriter>
  40. static void decode(CharReader , ByteWriter );
  41. template<class CharReader, class ByteWriter>
  42. static status_t try_decode(CharReader , ByteWriter );
  43. static __VIC_CONSTEXPR_FUNC size_t encoded_length(size_t orig_len)
  44. {
  45. return (orig_len + 2) / 3 * 4; // ceil(orig_len / 3.0) * 4
  46. }
  47. static __VIC_CONSTEXPR_FUNC size_t max_decoded_length(size_t orig_len)
  48. {
  49. // TODO: the actual value depends on trailing '='
  50. return orig_len / 4 * 3;
  51. }
  52. };
  53. //////////////////////////////////////////////////////////////////////////////
  54. //----------------------------------------------------------------------------
  55. template<class ByteReader, class CharWriter>
  56. void base64::encode(ByteReader r, CharWriter w)
  57. {
  58. unsigned char triad[3];
  59. int pos = 0;
  60. while(r.read(triad[pos]))
  61. {
  62. if(pos == 2)
  63. {
  64. w.write(abc[triad[0] >> 2]);
  65. w.write(abc[((triad[0] & 0x03) << 4) | (triad[1] >> 4)]);
  66. w.write(abc[((triad[1] & 0x0F) << 2) | (triad[2] >> 6)]);
  67. w.write(abc[triad[2] & 0x3F]);
  68. pos = 0;
  69. }
  70. else pos++;
  71. }
  72. if(pos > 0)
  73. {
  74. w.write(abc[triad[0] >> 2]);
  75. if(pos == 2)
  76. {
  77. w.write(abc[((triad[0] & 0x03) << 4) | (triad[1] >> 4)]);
  78. w.write(abc[(triad[1] & 0x0F) << 2]);
  79. }
  80. else // if(pos == 1)
  81. {
  82. w.write(abc[(triad[0] & 0x03) << 4]);
  83. w.write('=');
  84. }
  85. w.write('=');
  86. }
  87. }
  88. //----------------------------------------------------------------------------
  89. template<class CharReader, class ByteWriter>
  90. base64::status_t base64::try_decode(CharReader r, ByteWriter w)
  91. {
  92. char quad[4];
  93. unsigned char code[4];
  94. int pos = 0;
  95. char ch;
  96. while(r.read(ch))
  97. {
  98. quad[pos] = ch;
  99. if(pos == 3)
  100. {
  101. for(int i=0; i<4; i++)
  102. {
  103. if(quad[i] != '=')
  104. {
  105. const char *p = static_cast<const char *>(
  106. std::memchr(abc, quad[i], sizeof abc));
  107. if(!p) return status::invalid_digit;
  108. code[i] = static_cast<unsigned char>(p - abc);
  109. }
  110. else code[i] = 255;
  111. }
  112. w.write((code[0] << 2) | (code[1] >> 4));
  113. if(code[2] != 255)
  114. {
  115. w.write(((code[1] & 0x0F) << 4) | (code[2] >> 2));
  116. if(code[3] != 255)
  117. w.write(((code[2] & 0x03) << 6) | code[3]);
  118. }
  119. pos = 0;
  120. }
  121. else pos++;
  122. }
  123. if(pos) return status::invalid_length;
  124. return status::ok;
  125. }
  126. //----------------------------------------------------------------------------
  127. template<class CharReader, class ByteWriter>
  128. void base64::decode(CharReader r, ByteWriter w)
  129. {
  130. switch(base64::try_decode(__VIC_STD_MOVE(r), __VIC_STD_MOVE(w)))
  131. {
  132. case status::ok: return;
  133. case status::invalid_length: throw bad_length();
  134. case status::invalid_digit: throw bad_digit();
  135. }
  136. // UNREACHABLE
  137. }
  138. //----------------------------------------------------------------------------
  139. } // namespace
  140. #endif // header guard