base32.odin 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // Base32 encoding/decoding implementation as specified in RFC 4648.
  2. // [[ More; https://www.rfc-editor.org/rfc/rfc4648.html ]]
  3. package encoding_base32
  4. // @note(zh): Encoding utility for Base32
  5. // A secondary param can be used to supply a custom alphabet to
  6. // @link(encode) and a matching decoding table to @link(decode).
  7. // If none is supplied it just uses the standard Base32 alphabet.
  8. // In case your specific version does not use padding, you may
  9. // truncate it from the encoded output.
  10. // Error represents errors that can occur during base32 decoding operations.
  11. // As per RFC 4648:
  12. // - Section 3.3: Invalid character handling
  13. // - Section 3.2: Padding requirements
  14. // - Section 6: Base32 encoding specifics (including block size requirements)
  15. Error :: enum {
  16. None,
  17. Invalid_Character, // Input contains characters outside the specified alphabet
  18. Invalid_Length, // Input length is not valid for base32 (must be a multiple of 8 with proper padding)
  19. Malformed_Input, // Input has improper structure (wrong padding position or incomplete groups)
  20. }
  21. Validate_Proc :: #type proc(c: byte) -> bool
  22. @private
  23. _validate_default :: proc(c: byte) -> bool {
  24. return (c >= 'A' && c <= 'Z') || (c >= '2' && c <= '7')
  25. }
  26. @(rodata)
  27. ENC_TABLE := [32]byte {
  28. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
  29. 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  30. 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
  31. 'Y', 'Z', '2', '3', '4', '5', '6', '7',
  32. }
  33. PADDING :: '='
  34. @(rodata)
  35. DEC_TABLE := [256]u8 {
  36. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  37. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  38. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  39. 0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
  40. 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  41. 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
  42. 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
  43. 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
  44. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  45. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  46. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  47. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  48. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  49. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  50. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  51. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  52. }
  53. encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
  54. out_length := (len(data) + 4) / 5 * 8
  55. out := make([]byte, out_length, allocator)
  56. _encode(out, data, ENC_TBL)
  57. return string(out[:])
  58. }
  59. @private
  60. _encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
  61. out := out
  62. data := data
  63. for len(data) > 0 {
  64. carry: byte
  65. switch len(data) {
  66. case:
  67. out[7] = ENC_TBL[data[4] & 0x1f]
  68. carry = data[4] >> 5
  69. fallthrough
  70. case 4:
  71. out[6] = ENC_TBL[carry | (data[3] << 3) & 0x1f]
  72. out[5] = ENC_TBL[(data[3] >> 2) & 0x1f]
  73. carry = data[3] >> 7
  74. fallthrough
  75. case 3:
  76. out[4] = ENC_TBL[carry | (data[2] << 1) & 0x1f]
  77. carry = (data[2] >> 4) & 0x1f
  78. fallthrough
  79. case 2:
  80. out[3] = ENC_TBL[carry | (data[1] << 4) & 0x1f]
  81. out[2] = ENC_TBL[(data[1] >> 1) & 0x1f]
  82. carry = (data[1] >> 6) & 0x1f
  83. fallthrough
  84. case 1:
  85. out[1] = ENC_TBL[carry | (data[0] << 2) & 0x1f]
  86. out[0] = ENC_TBL[data[0] >> 3]
  87. }
  88. if len(data) < 5 {
  89. out[7] = byte(PADDING)
  90. if len(data) < 4 {
  91. out[6] = byte(PADDING)
  92. out[5] = byte(PADDING)
  93. if len(data) < 3 {
  94. out[4] = byte(PADDING)
  95. if len(data) < 2 {
  96. out[3] = byte(PADDING)
  97. out[2] = byte(PADDING)
  98. }
  99. }
  100. }
  101. break
  102. }
  103. data = data[5:]
  104. out = out[8:]
  105. }
  106. }
  107. @(optimization_mode="favor_size")
  108. decode :: proc(
  109. data: string,
  110. DEC_TBL := DEC_TABLE,
  111. validate: Validate_Proc = _validate_default,
  112. allocator := context.allocator) -> (out: []byte, err: Error) {
  113. if len(data) == 0 {
  114. return nil, .None
  115. }
  116. // Check minimum length requirement first
  117. if len(data) < 2 {
  118. return nil, .Invalid_Length
  119. }
  120. // Validate characters using provided validation function
  121. for i := 0; i < len(data); i += 1 {
  122. c := data[i]
  123. if c == byte(PADDING) {
  124. break
  125. }
  126. if !validate(c) {
  127. return nil, .Invalid_Character
  128. }
  129. }
  130. // Validate padding and length
  131. data_len := len(data)
  132. padding_count := 0
  133. for i := data_len - 1; i >= 0; i -= 1 {
  134. if data[i] != byte(PADDING) {
  135. break
  136. }
  137. padding_count += 1
  138. }
  139. // Check for proper padding and length combinations
  140. if padding_count > 0 {
  141. // Verify no padding in the middle
  142. for i := 0; i < data_len - padding_count; i += 1 {
  143. if data[i] == byte(PADDING) {
  144. return nil, .Malformed_Input
  145. }
  146. }
  147. content_len := data_len - padding_count
  148. mod8 := content_len % 8
  149. required_padding: int
  150. switch mod8 {
  151. case 2: required_padding = 6 // 2 chars need 6 padding chars
  152. case 4: required_padding = 4 // 4 chars need 4 padding chars
  153. case 5: required_padding = 3 // 5 chars need 3 padding chars
  154. case 7: required_padding = 1 // 7 chars need 1 padding char
  155. case: required_padding = 0
  156. }
  157. if required_padding > 0 {
  158. if padding_count != required_padding {
  159. return nil, .Malformed_Input
  160. }
  161. } else if mod8 != 0 {
  162. return nil, .Malformed_Input
  163. }
  164. } else {
  165. // No padding - must be multiple of 8
  166. if data_len % 8 != 0 {
  167. return nil, .Malformed_Input
  168. }
  169. }
  170. // Calculate decoded length: 5 bytes for every 8 input chars
  171. input_chars := data_len - padding_count
  172. out_len := input_chars * 5 / 8
  173. out = make([]byte, out_len, allocator)
  174. defer if err != .None {
  175. delete(out)
  176. }
  177. // Process input in 8-byte blocks
  178. outi := 0
  179. for i := 0; i < input_chars; i += 8 {
  180. buf: [8]byte
  181. block_size := min(8, input_chars - i)
  182. // Decode block
  183. for j := 0; j < block_size; j += 1 {
  184. buf[j] = DEC_TBL[data[i + j]]
  185. }
  186. // Convert to output bytes based on block size
  187. bytes_to_write := block_size * 5 / 8
  188. switch block_size {
  189. case 8:
  190. out[outi + 4] = (buf[6] << 5) | buf[7]
  191. fallthrough
  192. case 7:
  193. out[outi + 3] = (buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3)
  194. fallthrough
  195. case 5:
  196. out[outi + 2] = (buf[3] << 4) | (buf[4] >> 1)
  197. fallthrough
  198. case 4:
  199. out[outi + 1] = (buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4)
  200. fallthrough
  201. case 2:
  202. out[outi] = (buf[0] << 3) | (buf[1] >> 2)
  203. }
  204. outi += bytes_to_write
  205. }
  206. return
  207. }