 
 <PRIVATE
 
-: read1-ignoring ( ignoring -- ch )
-    read1 2dup swap member? [ drop read1-ignoring ] [ nip ] if ;
+: read1-ignoring ( ignoring stream -- ch )
+    dup stream-read1 pick dupd member?
+    [ drop read1-ignoring ] [ 2nip ] if ;
 
-: read-ignoring ( ignoring n -- str )
-    [ drop read1-ignoring ] with { } map-integers
-    [ { f 0 } member? not ] filter
-    [ f ] [ >string ] if-empty ;
+: read-ignoring ( n ignoring stream -- str )
+    '[ _ _ read1-ignoring ] replicate
+    [ { f 0 } member-eq? not ] "" filter-as
+    [ f ] when-empty ;
 
 : ch>base64 ( ch -- ch )
     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"


SYMBOL: column

: write1-lines ( ch -- )
    write1
    column get [
        1 + [ 76 = [ B{ CHAR: \r CHAR: \n } write ] when ]
        [ 76 mod column set ] bi
    ] when* ;

: write-lines ( str -- )
    [ write1-lines ] each ;

: encode3 ( seq -- )
    be> 4 iota <reversed> [
        -6 * shift 0x3f bitand ch>base64 write1-lines
    ] with each ; inline

: encode-pad ( seq n -- )
    [ 3 0 pad-tail binary [ encode3 ] with-byte-writer ]

: decode4 ( seq -- )
    [ 0 [ base64>ch swap 6 shift bitor ] reduce 3 >be ]
    [ [ CHAR: = = ] count ] bi head-slice*
    output-stream get '[ _ stream-write1 ] each ; inline

: (encode-base64) ( stream -- )
    3 over stream-read dup length {
        { 0 [ 2drop ] }
        { 3 [ encode3 (encode-base64) ] }
        [ encode-pad (encode-base64) ]
    } case ;

PRIVATE>

: encode-base64 ( -- )
    input-stream get (encode-base64) ;

: encode-base64-lines ( -- )
    0 column [ encode-base64 ] with-variable ;

<PRIVATE

: (decode-base64) ( stream -- )
    4 "\n\r" pick read-ignoring dup length {
        { 0 [ 2drop ] }
        { 4 [ decode4 (decode-base64) ] }
        [ malformed-base64 ]
    } case ;

PRIVATE>

: decode-base64 ( -- )
    input-stream get (decode-base64) ;

: >base64 ( seq -- base64 )
    binary [ binary [ encode-base64 ] with-byte-reader ] with-byte-writer ;
