base64.bmx 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. ' Copyright (c) 2008-2019 Bruce A Henderson
  2. '
  3. ' Permission is hereby granted, free of charge, to any person obtaining a copy
  4. ' of this software and associated documentation files (the "Software"), to deal
  5. ' in the Software without restriction, including without limitation the rights
  6. ' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. ' copies of the Software, and to permit persons to whom the Software is
  8. ' furnished to do so, subject to the following conditions:
  9. '
  10. ' The above copyright notice and this permission notice shall be included in
  11. ' all copies or substantial portions of the Software.
  12. '
  13. ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. ' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. ' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. ' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. ' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. ' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. ' THE SOFTWARE.
  20. '
  21. SuperStrict
  22. Rem
  23. bbdoc: Base64 Encoding
  24. End Rem
  25. Module BRL.Base64
  26. ModuleInfo "Version: 1.01"
  27. ModuleInfo "License: MIT"
  28. ModuleInfo "Copyright: Original - Robert Harder (http://iharder.sourceforge.net/current/java/base64/)"
  29. ModuleInfo "Copyright: BlitzMax port - 2008-2019 Bruce A Henderson"
  30. ModuleInfo "History: 1.01"
  31. ModuleInfo "History: Fixed Encode() sometimes returning an extra null character."
  32. ModuleInfo "History: 1.00 Initial Release"
  33. Rem
  34. bbdoc: Encode/Decode Base64 data.
  35. about:
  36. End Rem
  37. Type TBase64
  38. ' Maximum line length (76) of Base64 output.
  39. Const MAX_LINE_LENGTH:Int = 76
  40. ' The equals sign (=) as int.
  41. Const EQUALS_SIGN:Int = Asc("=")
  42. ' The new line character (\n) as int.
  43. Const NEW_LINE:Int = Asc("~n")
  44. Const WHITE_SPACE_ENC:Int = -5 ' Indicates white space in encoding
  45. Const EQUALS_SIGN_ENC:Int = -1 ' Indicates equals sign in encoding
  46. Private
  47. Const _STANDARD_ALPHABET:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  48. Global _STANDARD_DECODABET:Int[] = [-9,-9,-9,-9,-9,-9,-9,-9,-9, ..
  49. -5,-5, ..
  50. -9,-9, ..
  51. -5, ..
  52. -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, ..
  53. -9,-9,-9,-9,-9, ..
  54. -5, ..
  55. -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, ..
  56. 62, ..
  57. -9,-9,-9, ..
  58. 63, ..
  59. 52,53,54,55,56,57,58,59,60,61, ..
  60. -9,-9,-9, ..
  61. -1, ..
  62. -9,-9,-9, ..
  63. 0,1,2,3,4,5,6,7,8,9,10,11,12,13, ..
  64. 14,15,16,17,18,19,20,21,22,23,24,25, ..
  65. -9,-9,-9,-9,-9,-9, ..
  66. 26,27,28,29,30,31,32,33,34,35,36,37,38, ..
  67. 39,40,41,42,43,44,45,46,47,48,49,50,51, ..
  68. -9,-9,-9,-9]
  69. Public
  70. Rem
  71. bbdoc: Encodes byte array data @source to a Base64 encoded String, starting at @offset.
  72. End Rem
  73. Function Encode:String(source:Byte[], offset:Int = 0, options:EBase64Options = EBase64Options.None)
  74. Return Encode(source, source.length, offset, options)
  75. End Function
  76. Rem
  77. bbdoc: Encodes byte data @source to a Base64 encoded String, starting at @offset and of @length bytes.
  78. End Rem
  79. Function Encode:String(source:Byte Ptr, length:Int, offset:Int = 0, options:EBase64Options = EBase64Options.None)
  80. ' Convert option To boolean in way that code likes it.
  81. Local breakLines:Int = True
  82. If options & EBase64Options.DontBreakLines Then
  83. breakLines = False
  84. End If
  85. Local len43:Int = length * 4 / 3
  86. Local nl:Int = len43 / MAX_LINE_LENGTH
  87. Local pad:Int = 0
  88. If length Mod 3 > 0 Then
  89. pad = 4
  90. End If
  91. If Not breakLines Then
  92. nl = 0
  93. End If
  94. Local outBuff:Byte[] = New Byte[ len43 + pad + nl ]
  95. Local d:Int = 0
  96. Local e:Int = 0
  97. Local len2:Int = length - 2
  98. Local lineLength:Int = 0
  99. While d < len2
  100. encode3to4( source, d + offset, 3, outBuff, e )
  101. lineLength :+ 4
  102. If breakLines And (lineLength = MAX_LINE_LENGTH) Then
  103. outBuff[e+4] = NEW_LINE
  104. e:+1
  105. lineLength = 0
  106. End If
  107. d:+3
  108. e:+4
  109. Wend
  110. ' pad?
  111. If d < Length Then
  112. encode3to4( source, d + offset, Length - d, outBuff, e )
  113. e :+ 4
  114. End If
  115. Return String.FromBytes(outBuff, e)
  116. End Function
  117. Rem
  118. bbdoc: Decodes Base64 encoded String @source to an array of Bytes, starting at @offset.
  119. End Rem
  120. Function Decode:Byte[]( source:String, offset:Int = 0, options:EBase64Options = EBase64Options.None )
  121. Local length:Int = source.length
  122. Local len34:Int = Length * 3 / 4
  123. Local outBuff:Byte[] = New Byte[ len34 ]
  124. Local outBuffPosn:Int = 0
  125. Local b4:Byte[] = New Byte[4]
  126. Local b4Posn:Int = 0
  127. Local sbiCrop:Int = 0
  128. Local sbiDecode:Int = 0
  129. For Local i:Int = offset Until offset + length
  130. sbiCrop = source[i] & $7f
  131. sbiDecode = _STANDARD_DECODABET[ sbiCrop ]
  132. If sbiDecode >= WHITE_SPACE_ENC Then
  133. If sbiDecode >= EQUALS_SIGN_ENC Then
  134. b4[ b4Posn ] = sbiCrop
  135. b4Posn:+1
  136. If b4Posn > 3 Then
  137. outBuffPosn :+ decode4to3( b4, 0, outBuff, outBuffPosn )
  138. b4Posn = 0
  139. ' If that was the equals sign, break out of 'for' loop
  140. If sbiCrop = EQUALS_SIGN Then
  141. Exit
  142. End If
  143. End If
  144. End If
  145. Else
  146. Throw "Bad Base64 input character at " + i + ": " + source[i]
  147. End If
  148. Next
  149. Return outBuff[0..outBuffPosn]
  150. End Function
  151. Private
  152. Function encode3to4(source:Byte Ptr, srcOffset:Int, numSigBytes:Int, destination:Byte Ptr, destOffset:Int)
  153. Local inBuff:Int
  154. If numSigBytes > 0 Then
  155. inBuff = (source[ srcOffset ] Shl 24) Shr 8
  156. If numSigBytes > 1 Then
  157. inBuff :| (source[ srcOffset + 1 ] Shl 24) Shr 16
  158. If numSigBytes > 2 Then
  159. inBuff :| (source[ srcOffset + 2 ] Shl 24) Shr 24
  160. End If
  161. End If
  162. End If
  163. Select numSigBytes
  164. Case 3
  165. destination[ destOffset ] = _STANDARD_ALPHABET[ (inBuff Shr 18) ]
  166. destination[ destOffset + 1 ] = _STANDARD_ALPHABET[ (inBuff Shr 12) & $3f ]
  167. destination[ destOffset + 2 ] = _STANDARD_ALPHABET[ (inBuff Shr 6) & $3f ]
  168. destination[ destOffset + 3 ] = _STANDARD_ALPHABET[ (inBuff ) & $3f ]
  169. Return
  170. Case 2
  171. destination[ destOffset ] = _STANDARD_ALPHABET[ (inBuff Shr 18) ]
  172. destination[ destOffset + 1 ] = _STANDARD_ALPHABET[ (inBuff Shr 12) & $3f ]
  173. destination[ destOffset + 2 ] = _STANDARD_ALPHABET[ (inBuff Shr 6) & $3f ]
  174. destination[ destOffset + 3 ] = EQUALS_SIGN
  175. Return
  176. Case 1
  177. destination[ destOffset ] = _STANDARD_ALPHABET[ (inBuff Shr 18) ]
  178. destination[ destOffset + 1 ] = _STANDARD_ALPHABET[ (inBuff Shr 12) & $3f ]
  179. destination[ destOffset + 2 ] = EQUALS_SIGN
  180. destination[ destOffset + 3 ] = EQUALS_SIGN
  181. Return
  182. End Select
  183. End Function
  184. Function decode4to3:Int( source:Byte Ptr, srcOffset:Int, destination:Byte Ptr , destOffset:Int)
  185. ' Example: Dk==
  186. If source[ srcOffset + 2] = EQUALS_SIGN Then
  187. Local outBuff:Int = ( ( _STANDARD_DECODABET[ source[ srcOffset ] ] & $FF ) Shl 18 ) ..
  188. | ( ( _STANDARD_DECODABET[ source[ srcOffset + 1] ] & $FF ) Shl 12 )
  189. destination[ destOffset ] = outBuff Shr 16
  190. Return 1
  191. ' Example: DkL=
  192. Else If source[ srcOffset + 3 ] = EQUALS_SIGN Then
  193. Local outBuff:Int = ( ( _STANDARD_DECODABET[ source[ srcOffset ] ] & $FF ) Shl 18 ) ..
  194. | ( ( _STANDARD_DECODABET[ source[ srcOffset + 1 ] ] & $FF ) Shl 12 ) ..
  195. | ( ( _STANDARD_DECODABET[ source[ srcOffset + 2 ] ] & $FF ) Shl 6 )
  196. destination[ destOffset ] = outBuff Shr 16
  197. destination[ destOffset + 1 ] = outBuff Shr 8
  198. Return 2
  199. ' Example: DkLE
  200. Else
  201. Local outBuff:Int = ( ( _STANDARD_DECODABET[ source[ srcOffset ] ] & $FF ) Shl 18 ) ..
  202. | ( ( _STANDARD_DECODABET[ source[ srcOffset + 1 ] ] & $FF ) Shl 12 ) ..
  203. | ( ( _STANDARD_DECODABET[ source[ srcOffset + 2 ] ] & $FF ) Shl 6) ..
  204. | ( ( _STANDARD_DECODABET[ source[ srcOffset + 3 ] ] & $FF ) )
  205. destination[ destOffset ] = outBuff Shr 16
  206. destination[ destOffset + 1 ] = outBuff Shr 8
  207. destination[ destOffset + 2 ] = outBuff
  208. Return 3
  209. End If
  210. End Function
  211. Public
  212. End Type
  213. Rem
  214. bbdoc: TBase64 options.
  215. End Rem
  216. Enum EBase64Options Flags
  217. None = 0
  218. Rem
  219. bbdoc: Encode to a single line, regardless of length.
  220. about: Otherwise, lines are typically wrapped at 76 characters.
  221. End Rem
  222. DontBreakLines = 8
  223. End Enum