Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
StringIO
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\ndef\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
@devinus

This comment has been minimized.

Copy link

commented Feb 6, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.