;; Copyright (c) 2004 James Bailey (dgym.REMOVE_THIS.bailey@gmail.com).
;;
;; Permission is hereby granted, free of charge, to any person obtaining a
;; copy of this software and associated documentation files (the "Software"), to
;; deal in the Software without restriction, including without limitation the
;; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
;; sell copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;;
;; The above copyright notice and this permission notice shall be included in all
;; copies or substantial portions of the Software.
;;
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
;; SOFTWARE.

;; base64 routines for bigloo, apart from the module info, bit routines, "when"
;; and fixed division "/fx" it should be slightly portable

;; ported to CHICKEN by felix
;; C implementation by zbigniew [2008]

(declare
 (fixnum)
 (export base64:encode base64:decode))

#>
static char enc_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static char dec_table[] = {
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  62, 0,  0,  0,  63,
     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0,  0,  0,  0,  0,  0, 
     0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14,
     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,  0,  0,  0,  0, 
     0,  26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 
     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
};

static void base64_encode(char *dst, const char *src, int len) {
    int i, o, r;
    for (i = 0, o = 0, r = len; i < len; i+= 3, o+= 4, r -= 3) {
        int n = src[i] << 16;
        if (r > 1)
            n |= src[i+1] << 8;
        dst[o] = enc_table[(n >> 18) & 63];
        dst[o+1] = enc_table[(n >> 12) & 63];
        if (r > 1) {
            if (r > 2) {
                n |= src[i+2];
                dst[o+3] = enc_table[n & 63];
            }
            dst[o+2] = enc_table[(n >> 6) & 63];
        }
    }

    switch(r) {
      case -2: dst[o-2] = '=';
      case -1: dst[o-1] = '=';
    }
}                                                                

static void base64_decode(char *dst, const char *src, int len) {
    int i, o, r;
    for (i = 0, o = 0, r = len; i < len; i += 4, o += 3, r -= 3) {
        int n = dec_table[src[i]] << 18 | dec_table[src[i+1]] << 12 |
                dec_table[src[i+2]] << 6| dec_table[src[i+3]];
        dst[o] = (n >> 16) & 255;
        if (r > 1) {
            dst[o+1] = (n >> 8) & 255;
            if (r > 2) {
                dst[o+2] = n & 255;
            }
        }
    }
}                                                                

<#

(define (base64:encode str)
  (define base64_encode (foreign-lambda void base64_encode pointer c-string int))
  (let* ((len (string-length str))
         (buf (##sys#make-string (* 4 (/ (+ len 2) 3))
                                 #f)))
    (base64_encode buf str len)
    buf))

(define (base64:decode str)
  (define base64_decode (foreign-lambda void base64_decode pointer c-string int))
  (let* ((len (string-length str))
         (result-len (- (* 3 (fx/ len 4))
                        (cond ((char=? (string-ref str (- len 2)) #\=) 2)
                              ((char=? (string-ref str (- len 1)) #\=) 1)
                              (else 0))))
         (buf (##sys#make-string result-len #f)))
    (base64_decode buf str len)
    buf))
