@@ -0,0 +1,186 @@
defmodule StringIO do
def new ( string ) when is_binary ( string ) do
spawn_link ( fn -> string_io_process ( string ) end )
end
def string_io_process ( string ) do
loop ( :infinity , String . to_char_list! ( string ) )
end
def loop ( wait , buf ) do
receive do
{ :io_request , from , reply_as , req } ->
p = :erlang . process_flag ( :priority , :normal )
buf = io_request ( from , reply_as , req , buf )
:erlang . process_flag ( :priority , p )
loop ( wait , buf )
:stop ->
receive after: ( 2 -> :ok )
:erlang . process_flag ( :priority , :low )
loop ( 0 , buf )
_ ->
loop ( 0 , buf )
end
end
defp io_request ( from , reply_as , req , buf ) do
{ reply , buf1 } = io_request ( req , buf )
io_reply ( from , reply_as , reply )
buf1
end
defp io_reply ( from , reply_as , reply ) do
send from , { :io_reply , reply_as , reply }
end
defp io_request ( { :put_chars , chars } , buf ) do
{ :ok , [ chars | buf ] }
end
defp io_request ( { :put_chars , m , f , as } , buf ) do
chars = apply ( m , f , as )
{ :ok , [ chars | buf ] }
end
defp io_request ( { :put_chars , _enc , chars } , buf ) do
io_request ( { :put_chars , chars } , buf )
end
defp io_request ( { :put_chars , _enc , mod , func , args } , buf ) do
io_request ( { :put_chars , mod , func , args } , buf )
end
defp io_request ( { :get_chars , _enc , prompt , n } , buf ) when n >= 0 do
io_request ( { :get_chars , prompt , n } , buf )
end
defp io_request ( { :get_chars , _prompt , n } , buf ) when n >= 0 do
get_chars ( n , buf )
end
defp io_request ( { :get_line , _enc , prompt } , buf ) do
io_request ( { :get_line , prompt } , buf )
end
defp io_request ( { :get_line , _prompt } , buf ) do
get_line ( buf )
end
defp io_request ( { :get_until , _encoding , prompt , mod , fun , args } , buf ) do
io_request ( { :get_until , prompt , mod , fun , args } , buf )
end
defp io_request ( { :get_until , _prompt , mod , fun , args } , buf ) do
get_until ( mod , fun , args , buf )
end
defp io_request ( { :setopts , _opts } , buf ) do
{ :ok , buf }
end
defp io_request ( :getopts , buf ) do
{ { :error , :enotsup } , buf }
end
defp io_request ( { :get_geometry , :columns } , buf ) do
{ { :error , :enotsup } , buf }
end
defp io_request ( { :get_geometry , :rows } , buf ) do
{ { :error , :enotsup } , buf }
end
defp io_request ( { :requests , reqs } , buf ) do
io_requests ( reqs , { :ok , buf } )
end
defp io_request ( _ , buf ) do
{ { :error , :request } , buf }
end
defp io_requests ( [ r | rs ] , { :ok , buf } ) do
io_requests ( rs , io_request ( r , buf ) )
end
defp io_requests ( _ , result ) do
result
end
defp get_line ( buf ) do
case buf do
[ ] ->
{ :eof , [ ] }
_ ->
{ line , rest } = Enum . split_while ( buf , fn ( char ) -> char != ?\n end )
case rest do
[ ] ->
{ String . from_char_list! ( line ) , [ ] }
[ _ | t ] ->
{ String . from_char_list! ( line ) <> "\n " , t }
end
end
end
defp get_chars ( n , buf ) do
case buf do
[ ] ->
{ :eof , [ ] }
_ ->
{ chars , rest } = Enum . split ( buf , n )
{ String . from_char_list! ( chars ) , rest }
end
end
defp get_until ( mod , fun , args , buf ) do
do_get_until ( buf , mod , fun , args )
end
defp do_get_until ( [ ] , mod , fun , args , continuation \\ [ ] )
defp do_get_until ( [ ] , mod , fun , args , continuation ) do
case apply ( mod , fun , [ continuation , :eof | args ] ) do
{ :done , result , rest_chars } ->
{ result , rest_chars }
{ :more , next_continuation } ->
do_get_until ( [ ] , mod , fun , args , next_continuation )
end
end
defp do_get_until ( input , mod , fun , args , continuation ) do
{ line , rest } = Enum . split_while ( input , fn ( char ) -> char != ?\n end )
case rest do
[ ] ->
case apply ( mod , fun , [ continuation , line | args ] ) do
{ :done , result , rest_chars } ->
{ result , rest_chars }
{ :more , next_continuation } ->
do_get_until ( [ ] , mod , fun , args , next_continuation )
end
[ _ | t ] ->
case apply ( mod , fun , [ continuation , line ++ '\n ' | args ] ) do
{ :done , result , rest_chars } ->
{ result , rest_chars ++ t }
{ :more , next_continuation } ->
do_get_until ( t , mod , fun , args , next_continuation )
end
end
end
end
ExUnit . start
defmodule StringIOTest do
use ExUnit.Case
test "the truth" do
f = StringIO . new "abc\n def\n "
assert IO . read ( f , :line ) == "abc\n "
assert IO . read ( f , :line ) == "def\n "
assert IO . read ( f , :line ) == :eof
assert IO . write ( f , "hello\n " ) == :ok
assert IO . read ( f , :line ) == "hello\n "
assert IO . read ( f , :line ) == :eof
end
end