BASE64

BASE64エンコード関数を使いたくなったので作成。

;; 8byte整数を等価なビットリストに変換
;; ex: 10 = #b00001010 => (0 0 0 0 1 0 1 0)
(defun octet-to-bytes (octet)
  (loop FOR i FROM 7 DOWNTO 0
        COLLECT (ldb (byte 1 i) octet)))

;; listがdivisorで割り切れない場合、足りない分だけpadding-valueで埋める
(defun padding (list divisor padding-value)
  (append list
    (loop REPEAT (abs (nth-value 1 (ceiling (length list) divisor)))
          COLLECT padding-value)))

(defun cdddddr (list) (nthcdr 6 list))

;; バイト列をBASE64文字列に変換する
(let ((table "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"))
  (defun octets-to-base64 (octets)
    (let ((bytes 
           ;; バイト列をビットリストに変換
           (loop FOR octet ACROSS octets
                 NCONC (octet-to-bytes octet))))
      (coerce
       (padding
        ;; bytesから6ビットずつ取り出す 
        (loop FOR (a b c d e f) ON (padding bytes 6 0) BY #'cdddddr 
          COLLECT
          ;; 変換表を参照して、6ビット値を対応する文字に変換
          (aref table (+ (ash a 5) (ash b 4) (ash c 3)
                         (ash d 2) (ash e 1) (ash f 0))))
        4 #\=)  ;; 変換後の文字の数が4の倍数でない場合は、足りない分を#\=で埋める
       'string))))