Merge pull request #646 from onionhammer/patch-1

Added base64 encode overload for arrays of integers/bytes
This commit is contained in:
Andreas Rumpf 2013-10-30 10:10:11 -07:00
commit 2a1f8baac4

View file

@ -1,117 +1,127 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## This module implements a base64 encoder and decoder. ## This module implements a base64 encoder and decoder.
const const
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string = template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} =
## encodes `s` into base64 representation. After `lineLen` characters, a ## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added. ## `newline` is added.
var total = ((len(s) + 2) div 3) * 4 var total = ((len(s) + 2) div 3) * 4
var numLines = (total + lineLen - 1) div lineLen var numLines = (total + lineLen - 1) div lineLen
if numLines > 0: inc(total, (numLines-1) * newLine.len) if numLines > 0: inc(total, (numLines-1) * newLine.len)
result = newString(total) result = newString(total)
var i = 0 var i = 0
var r = 0 var r = 0
var currLine = 0 var currLine = 0
while i < s.len - 2: while i < s.len - 2:
var a = ord(s[i]) var a = ord(s[i])
var b = ord(s[i+1]) var b = ord(s[i+1])
var c = ord(s[i+2]) var c = ord(s[i+2])
result[r] = cb64[a shr 2] result[r] = cb64[a shr 2]
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] 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+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
result[r+3] = cb64[c and 0x3F] result[r+3] = cb64[c and 0x3F]
inc(r, 4) inc(r, 4)
inc(i, 3) inc(i, 3)
inc(currLine, 4) inc(currLine, 4)
if currLine >= lineLen and i != s.len-2: if currLine >= lineLen and i != s.len-2:
for x in items(newLine): for x in items(newLine):
result[r] = x result[r] = x
inc(r) inc(r)
currLine = 0 currLine = 0
if i < s.len-1: if i < s.len-1:
var a = ord(s[i]) var a = ord(s[i])
var b = ord(s[i+1]) var b = ord(s[i+1])
result[r] = cb64[a shr 2] result[r] = cb64[a shr 2]
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] 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+2] = cb64[((b and 0x0F) shl 2)]
result[r+3] = '=' result[r+3] = '='
if r+4 != result.len: if r+4 != result.len:
setLen(result, r+4) setLen(result, r+4)
elif i < s.len: elif i < s.len:
var a = ord(s[i]) var a = ord(s[i])
result[r] = cb64[a shr 2] result[r] = cb64[a shr 2]
result[r+1] = cb64[(a and 3) shl 4] result[r+1] = cb64[(a and 3) shl 4]
result[r+2] = '=' result[r+2] = '='
result[r+3] = '=' result[r+3] = '='
if r+4 != result.len: if r+4 != result.len:
setLen(result, r+4) setLen(result, r+4)
else: else:
assert(r == result.len) assert(r == result.len)
proc decodeByte(b: char): int {.inline.} = proc encode*[T:TInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
case b ## encodes `s` into base64 representation. After `lineLen` characters, a
of '+': result = ord('>') ## `newline` is added.
of '0'..'9': result = ord(b) + 4 encodeInternal(s, lineLen, newLine)
of 'A'..'Z': result = ord(b) - ord('A')
of 'a'..'z': result = ord(b) - 71 proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
else: result = 63 ## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added.
proc decode*(s: string): string = encodeInternal(s, lineLen, newLine)
## decodes a string in base64 representation back into its original form.
## Whitespace is skipped. proc decodeByte(b: char): int {.inline.} =
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'} case b
var total = ((len(s) + 3) div 4) * 3 of '+': result = ord('>')
# total is an upper bound, as we will skip arbitrary whitespace: of '0'..'9': result = ord(b) + 4
result = newString(total) of 'A'..'Z': result = ord(b) - ord('A')
of 'a'..'z': result = ord(b) - 71
var i = 0 else: result = 63
var r = 0
while true: proc decode*(s: string): string =
while s[i] in Whitespace: inc(i) ## decodes a string in base64 representation back into its original form.
if i < s.len-3: ## Whitespace is skipped.
var a = s[i].decodeByte const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
var b = s[i+1].decodeByte var total = ((len(s) + 3) div 4) * 3
var c = s[i+2].decodeByte # total is an upper bound, as we will skip arbitrary whitespace:
var d = s[i+3].decodeByte result = newString(total)
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03)) var i = 0
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F)) var r = 0
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F)) while true:
inc(r, 3) while s[i] in Whitespace: inc(i)
inc(i, 4) if i < s.len-3:
else: break var a = s[i].decodeByte
assert i == s.len var b = s[i+1].decodeByte
# adjust the length: var c = s[i+2].decodeByte
if i > 0 and s[i-1] == '=': var d = s[i+3].decodeByte
dec(r)
if i > 1 and s[i-2] == '=': dec(r) result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
setLen(result, r) result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
when isMainModule: inc(r, 3)
assert encode("leasure.") == "bGVhc3VyZS4=" inc(i, 4)
assert encode("easure.") == "ZWFzdXJlLg==" else: break
assert encode("asure.") == "YXN1cmUu" assert i == s.len
assert encode("sure.") == "c3VyZS4=" # adjust the length:
if i > 0 and s[i-1] == '=':
const longText = """Man is distinguished, not only by his reason, but by this dec(r)
singular passion from other animals, which is a lust of the mind, if i > 1 and s[i-2] == '=': dec(r)
that by a perseverance of delight in the continued and indefatigable setLen(result, r)
generation of knowledge, exceeds the short vehemence of any carnal
pleasure.""" when isMainModule:
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.", assert encode("leasure.") == "bGVhc3VyZS4="
"asure.", longText] assert encode("easure.") == "ZWFzdXJlLg=="
for t in items(tests): assert encode("asure.") == "YXN1cmUu"
assert decode(encode(t)) == t 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):
assert decode(encode(t)) == t