summary refs log tree commit diff stats
path: root/lib/pure/base64.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/base64.nim')
-rw-r--r--lib/pure/base64.nim129
1 files changed, 129 insertions, 0 deletions
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
new file mode 100644
index 000000000..a4673b5f7
--- /dev/null
+++ b/lib/pure/base64.nim
@@ -0,0 +1,129 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2010 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements a base64 encoder and decoder.
+
+const 
+  cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+proc encode*(s: string, lineLen = 75, newLine="\13\10"): string = 
+  ## encodes `s` into base64 representation. After `lineLen` characters, a 
+  ## `newline` is added.
+  var total = ((len(s) + 2) div 3) * 4
+  var numLines = (total + (lineLen - 1)) div lineLen
+  if numLines > 0: inc(total, (numLines-1) * newLine.len)

+
+  result = newString(total)
+  var i = 0
+  var r = 0
+  var currLine = 0
+  while i < s.len - 2:
+    var a = ord(s[i])
+    var b = ord(s[i+1])
+    var c = ord(s[i+2])
+    result[r] = cb64[a shr 2]
+    result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+    result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] 
+    result[r+3] = cb64[c and 0x3F] 
+    inc(r, 4)
+    inc(i, 3)
+    inc(currLine, 4)
+    if currLine >= lineLen and i != s.len-2: 
+      for x in items(newLine): 
+        result[r] = x
+        inc(r)
+      currLine = 0
+
+  if i < s.len-1:
+    var a = ord(s[i])
+    var b = ord(s[i+1])
+    result[r] = cb64[a shr 2]
+    result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+    result[r+2] = cb64[((b and 0x0F) shl 2)] 
+    result[r+3] = '='
+    assert(r+4 == result.len)
+  elif i < s.len:
+    var a = ord(s[i])
+    result[r] = cb64[a shr 2]
+    result[r+1] = cb64[(a and 3) shl 4]
+    result[r+2] = '='
+    result[r+3] = '='
+    assert(r+4 == result.len)
+  else:
+    assert(r == result.len)
+
+proc decodeByte(b: char): int {.inline.} = 
+  case b
+  of '+': result = ord('>')
+  of '0'..'9': result = ord(b) + 4
+  of 'A'..'Z': result = ord(b) - ord('A')
+  of 'a'..'z': result = ord(b) - 71
+  else: result = 63
+
+proc decode*(s: string): string = 
+  ## decodes a string in base64 representation back into its original form.
+  ## Whitespace is skipped.
+  const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
+  var total = ((len(s) + 3) div 4) * 3
+  # total is an upper bound, as we will skip arbitrary whitespace:
+  result = newString(total)
+
+  var i = 0
+  var r = 0
+  while true:
+    while s[i] in Whitespace: inc(i)
+    if i < s.len-3:
+      var a = s[i].decodeByte
+      var b = s[i+1].decodeByte
+      var c = s[i+2].decodeByte
+      var d = s[i+3].decodeByte
+      
+      result[r] = chr((a shl 2) or ((b shr 4) and 0x03))
+      result[r+1] = chr((b shl 4) or ((c shr 2) and 0x0F))
+      result[r+2] = chr((c shl 6) or (d and 0x3F))
+      inc(r, 3)
+      inc(i, 4)
+    else: break
+  if i < s.len-2 and s[i+2] == '=':
+    var a = s[i].decodeByte
+    var b = s[i+1].decodeByte
+    result[r] = chr(a shl 2 or ((b shr 4) and 3))
+    inc(r)
+  elif i < s.len-3 and s[i+3] == '=':
+    var a = s[i].decodeByte
+    var b = s[i+1].decodeByte
+    var c = s[i+2].decodeByte
+    result[r] = chr((a shl 2) or ((b shr 4) and 3))
+    result[r+1] = chr((b shl 4) or ((c shr 2) and 0xF))
+    inc(r, 2)
+  setLen(result, r)
+  
+when isMainModule:
+  assert encode("leasure.") == "bGVhc3VyZS4="
+  assert encode("easure.") == "ZWFzdXJlLg=="
+  assert encode("asure.") == "YXN1cmUu"
+  assert encode("sure.") == "c3VyZS4="
+  
+  const longText = """Man is distinguished, not only by his reason, but by this
+    singular passion from other animals, which is a lust of the mind, 
+    that by a perseverance of delight in the continued and indefatigable
+    generation of knowledge, exceeds the short vehemence of any carnal
+    pleasure."""
+  const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
+                 "asure.", longText]
+  for t in items(tests):
+    if decode(encode(t)) != t:
+      echo "##", t, "##"
+
+  echo encode("Man is distinguished, not only by his reason, but by this" &
+    " singular passion from other animals, which is a lust of the mind, " &
+    "that by a perseverance of delight in the continued and indefatigable" & 
+    " generation of knowledge, exceeds the short vehemence of any carnal" &
+    " pleasure.")
+