Base64.swift 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. //
  2. // Base64.swift
  3. // SwiftyBeaver (macOS)
  4. //
  5. // Copyright © 2017 Sebastian Kreutzberger. All rights reserved.
  6. //
  7. #if os(Linux)
  8. import Foundation
  9. struct InvalidBase64: Error {}
  10. struct Base64 {
  11. static func decode(_ string: String) throws -> [UInt8] {
  12. return try decode([UInt8](string.utf8))
  13. }
  14. /// Decodes a Base64 encoded String into Data
  15. ///
  16. /// - throws: If the string isn't base64 encoded
  17. static func decode(_ string: [UInt8]) throws -> [UInt8] {
  18. let lookupTable: [UInt8] = [
  19. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  20. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  21. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 62, 64, 63,
  22. 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
  23. 64, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14,
  24. 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 63,
  25. 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
  26. 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
  27. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  28. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  29. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  30. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  31. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  32. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  33. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  34. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  35. 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
  36. ]
  37. let remainder = string.count % 4
  38. let length = (string.count - remainder) / 4
  39. var decoded = [UInt8]()
  40. decoded.reserveCapacity(length)
  41. var index = 0
  42. var i0: UInt8 = 0
  43. var i1: UInt8 = 0
  44. var i2: UInt8 = 0
  45. var i3: UInt8 = 0
  46. while index &+ 4 < string.count {
  47. i0 = lookupTable[numericCast(string[index])]
  48. i1 = lookupTable[numericCast(string[index &+ 1])]
  49. i2 = lookupTable[numericCast(string[index &+ 2])]
  50. i3 = lookupTable[numericCast(string[index &+ 3])]
  51. if i0 > 63 || i1 > 63 || i2 > 63 || i3 > 63 {
  52. throw InvalidBase64()
  53. }
  54. decoded.append(i0 << 2 | i1 >> 4)
  55. decoded.append(i1 << 4 | i2 >> 2)
  56. decoded.append(i2 << 6 | i3)
  57. index += 4
  58. }
  59. if string.count &- index > 1 {
  60. i0 = lookupTable[numericCast(string[index])]
  61. i1 = lookupTable[numericCast(string[index &+ 1])]
  62. if i1 > 63 {
  63. guard string[index] == 61 else {
  64. throw InvalidBase64()
  65. }
  66. return decoded
  67. }
  68. if i2 > 63 {
  69. guard string[index &+ 2] == 61 else {
  70. throw InvalidBase64()
  71. }
  72. return decoded
  73. }
  74. decoded.append(i0 << 2 | i1 >> 4)
  75. if string.count &- index > 2 {
  76. i2 = lookupTable[numericCast(string[index &+ 2])]
  77. if i2 > 63 {
  78. guard string[index &+ 2] == 61 else {
  79. throw InvalidBase64()
  80. }
  81. return decoded
  82. }
  83. decoded.append(i1 << 4 | i2 >> 2)
  84. if string.count &- index > 3 {
  85. i3 = lookupTable[numericCast(string[index &+ 3])]
  86. if i3 > 63 {
  87. guard string[index &+ 3] == 61 else {
  88. throw InvalidBase64()
  89. }
  90. return decoded
  91. }
  92. decoded.append(i2 << 6 | i3)
  93. }
  94. }
  95. }
  96. return decoded
  97. }
  98. static func encode(_ data: [UInt8]) -> String {
  99. let base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  100. var encoded: String = ""
  101. func appendCharacterFromBase(_ character: Int) {
  102. encoded.append(base64[base64.index(base64.startIndex, offsetBy: character)])
  103. }
  104. func byte(_ index: Int) -> Int {
  105. return Int(data[index])
  106. }
  107. let decodedBytes = data.map { Int($0) }
  108. var i = 0
  109. while i < decodedBytes.count - 2 {
  110. appendCharacterFromBase((byte(i) >> 2) & 0x3F)
  111. appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
  112. appendCharacterFromBase(((byte(i + 1) & 0xF) << 2) | ((byte(i + 2) & 0xC0) >> 6))
  113. appendCharacterFromBase(byte(i + 2) & 0x3F)
  114. i += 3
  115. }
  116. if i < decodedBytes.count {
  117. appendCharacterFromBase((byte(i) >> 2) & 0x3F)
  118. if i == decodedBytes.count - 1 {
  119. appendCharacterFromBase(((byte(i) & 0x3) << 4))
  120. encoded.append("=")
  121. } else {
  122. appendCharacterFromBase(((byte(i) & 0x3) << 4) | ((byte(i + 1) & 0xF0) >> 4))
  123. appendCharacterFromBase(((byte(i + 1) & 0xF) << 2))
  124. }
  125. encoded.append("=")
  126. }
  127. return encoded
  128. }
  129. }
  130. #endif