encode.scm 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. (define-module (encode)
  2. #:use-module (rnrs base)
  3. #:use-module ((guile)
  4. #:select (lambda* λ
  5. when unless
  6. simple-format
  7. peek))
  8. #:use-module (ice-9 textual-ports)
  9. ;; SRFI 1: list procedures
  10. #:use-module (srfi srfi-1)
  11. #:use-module ((base-64) #:prefix base-64:)
  12. #:use-module (bit-integers)
  13. #:use-module (string-helpers)
  14. #:use-module (pipeline)
  15. #:export (encode))
  16. (define encode-6-bits
  17. (λ (bits)
  18. "Treat bits as an integer index into the vector of base 64 characters."
  19. (base-64:integer->char bits)))
  20. (define encode-3-chars
  21. (λ (chars)
  22. "Encode 3 ASCII characters to base 64 characters."
  23. (let ([as-ints (map char->integer chars)]
  24. [char-width 8])
  25. (let ([concattenated (concat-integers as-ints char-width)]
  26. ;; a character in base64 is represented by 6 bits
  27. [char-width-base64 6]
  28. ;; 63 has all positions set to 1
  29. [mask-6-bits 63])
  30. (list->string
  31. (list (encode-6-bits (bit-integer-get-range concattenated
  32. 6
  33. (* 3 char-width-base64)))
  34. (encode-6-bits (bit-integer-get-range concattenated
  35. 6
  36. (* 2 char-width-base64)))
  37. (encode-6-bits (bit-integer-get-range concattenated
  38. 6
  39. char-width-base64))
  40. (encode-6-bits (bit-integer-get-range concattenated
  41. 6
  42. 0))))))))
  43. (define encode
  44. (λ (text)
  45. ;; pad the string with zeros to have a string of a length divisible by 3
  46. (define text-length (string-length text))
  47. (define padding-char (integer->char 0))
  48. (define padding-count
  49. (let ([last-group-length (remainder text-length 3)])
  50. (if (> last-group-length 0)
  51. (- 3 last-group-length)
  52. 0)))
  53. (define padded-string
  54. (call-with-output-string
  55. (λ (port)
  56. (put-string port text)
  57. (let iter ([count° padding-count])
  58. (unless (= count° 0)
  59. (put-char port padding-char)
  60. (iter (- count° 1)))))))
  61. (let ([encoded-with-zeros
  62. (call-with-output-string
  63. (λ (port)
  64. (let iter ([chars° (string->list padded-string)])
  65. (unless (null? chars°)
  66. ;; (simple-format #t "not yet null: ~a\n" chars°)
  67. (put-string port (encode-3-chars (take chars° 3)))
  68. (iter (drop chars° 3))))))])
  69. ;; Remove the zeros which were only added to get a multiple of 3
  70. ;; in character count.
  71. (let ([encoded-without-zeros
  72. (substring encoded-with-zeros
  73. 0
  74. (- (string-length encoded-with-zeros) padding-count))])
  75. ;; Add the = characters, to indicate the used padding count in
  76. ;; the resulting string.
  77. (string-append encoded-without-zeros
  78. (string-repeat "=" padding-count))))))