1 ! Copyright (C) 2008 Daniel Ehrenberg.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: math kernel sequences sbufs vectors namespaces growable
4 strings io classes continuations destructors combinators
5 io.streams.plain splitting byte-arrays
6 sequences.private accessors ;
9 ! The encoding descriptor protocol
11 GENERIC: decode-char ( stream encoding -- char/f )
13 GENERIC: encode-char ( char stream encoding -- )
15 GENERIC: <decoder> ( stream encoding -- newstream )
17 CONSTANT: replacement-char HEX: fffd
19 TUPLE: decoder stream code cr ;
23 GENERIC: <encoder> ( stream encoding -- newstream )
25 TUPLE: encoder stream code ;
31 M: object <decoder> f decoder boa ;
35 : cr+ ( stream -- ) t >>cr drop ; inline
37 : cr- ( stream -- ) f >>cr drop ; inline
39 : >decoder< ( decoder -- stream encoding )
40 [ stream>> ] [ code>> ] bi ; inline
42 : fix-read1 ( stream char -- char )
50 M: decoder stream-element-type
53 M: decoder stream-tell stream>> stream-tell ;
55 M: decoder stream-seek stream>> stream-seek ;
57 M: decoder stream-read1
58 dup >decoder< decode-char fix-read1 ;
60 : fix-read ( stream string -- string )
64 over stream-read1 [ suffix ] when*
68 : (read) ( n quot -- n string )
71 over [ swapd set-nth-unsafe f ] [ 3drop t ] if
72 ] curry compose find-integer
75 : finish-read ( n string -- string/f )
77 { [ over 0 = ] [ 2drop f ] }
78 { [ over not ] [ nip ] }
82 M: decoder stream-read
83 [ nip ] [ >decoder< [ decode-char ] 2curry (read) finish-read ] 2bi
86 M: decoder stream-read-partial stream-read ;
88 : line-ends/eof ( stream str -- str ) f like swap cr- ; inline
90 : line-ends\r ( stream str -- str ) swap cr+ ; inline
92 : line-ends\n ( stream str -- str )
93 over cr>> over empty? and
94 [ drop dup cr- stream-readln ] [ swap cr- ] if ; inline
96 : handle-readln ( stream str ch -- str )
98 { f [ line-ends/eof ] }
99 { CHAR: \r [ line-ends\r ] }
100 { CHAR: \n [ line-ends\n ] }
103 : ((read-until)) ( buf quot: ( -- char stop? ) -- string/f sep/f )
105 [ [ drop "" like ] dip ]
106 [ pick push ((read-until)) ] if ; inline recursive
108 : (read-until) ( quot -- string/f sep/f )
109 100 <sbuf> swap ((read-until)) ; inline
111 : decoder-read-until ( seps stream encoding -- string/f sep/f )
112 [ decode-char dup [ dup rot member? ] [ 2drop f t ] if ] 3curry
115 M: decoder stream-read-until >decoder< decoder-read-until ;
117 : decoder-readln ( stream encoding -- string/f sep/f )
118 [ decode-char dup [ dup "\r\n" member? ] [ drop f t ] if ] 2curry
121 M: decoder stream-readln dup >decoder< decoder-readln handle-readln ;
123 M: decoder dispose stream>> dispose ;
126 M: object <encoder> encoder boa ;
128 : >encoder< ( encoder -- stream encoding )
129 [ stream>> ] [ code>> ] bi ; inline
131 M: encoder stream-element-type
134 M: encoder stream-write1
135 >encoder< encode-char ;
137 GENERIC# encoder-write 2 ( string stream encoding -- )
139 M: string encoder-write
140 [ encode-char ] 2curry each ;
142 M: encoder stream-write
143 >encoder< encoder-write ;
145 M: encoder dispose stream>> dispose ;
147 M: encoder stream-flush stream>> stream-flush ;
149 INSTANCE: encoder plain-writer
152 GENERIC# re-encode 1 ( stream encoding -- newstream )
154 M: object re-encode <encoder> ;
156 M: encoder re-encode [ stream>> ] dip re-encode ;
158 : encode-output ( encoding -- )
159 output-stream [ swap re-encode ] change ;
161 : with-encoded-output ( encoding quot -- )
162 [ [ output-stream get ] dip re-encode ] dip
163 with-output-stream* ; inline
165 GENERIC# re-decode 1 ( stream encoding -- newstream )
167 M: object re-decode <decoder> ;
169 M: decoder re-decode [ stream>> ] dip re-decode ;
171 : decode-input ( encoding -- )
172 input-stream [ swap re-decode ] change ;
174 : with-decoded-input ( encoding quot -- )
175 [ [ input-stream get ] dip re-decode ] dip
176 with-input-stream* ; inline