 
 CONSTANT: replacement-char HEX: fffd
 
-TUPLE: decoder stream code cr ;
+TUPLE: decoder { stream read-only } { code read-only } { cr boolean } ;
+INSTANCE: decoder noncopying-reader
 
 ERROR: decode-error ;
 
 GENERIC: <encoder> ( stream encoding -- newstream )
 
-TUPLE: encoder stream code ;
+TUPLE: encoder { stream read-only } { code read-only } ;
 
 ERROR: encode-error ;
 

USING: accessors combinators destructors io io.streams.plain
kernel math namespaces sbufs sequences sequences.private
splitting strings ;
IN: io.encodings

! The encoding descriptor protocol

    ] when nip ; inline

M: decoder stream-element-type
    drop +character+ ; inline

M: decoder stream-tell stream>> stream-tell ; inline

M: decoder stream-seek stream>> stream-seek ; inline

: (read1) ( decoder -- ch )
    >decoder< decode-char ; inline

:: fix-cr ( decoder c -- c' )
    decoder cr>> [
        decoder cr-
        c CHAR: \n eq? [ decoder (read1) ] [ c ] if
    ] [ c ] if ; inline

M: decoder stream-read1 ( decoder -- ch )
    dup (read1) fix-cr ; inline

:: (read) ( count n buf stream encoding -- count )
    count n = [ count ] [
        stream encoding decode-char [
            count buf set-nth-unsafe
            count 1 + n buf stream encoding (read)
        ] [ count ] if*
    ] if ; inline recursive

M:: decoder stream-read-unsafe ( n buf decoder -- count )
    n 0 = [ 0 ] [
        decoder >decoder< :> ( stream encoding )
        stream encoding decode-char :> c1
        decoder c1 fix-cr :> c1'
        c1' [
            c1' 0 buf set-nth-unsafe
            1 n buf stream encoding (read)
        ] [ 0 ] if
    ] if ; inline

M: decoder stream-read-partial-unsafe stream-read-unsafe ; inline

: line-ends/eof ( stream str -- str ) f like swap cr- ; inline

