Skip to content

Instantly share code, notes, and snippets.

@mururu
Created February 6, 2014 00:40
Show Gist options
  • Save mururu/8836338 to your computer and use it in GitHub Desktop.
Save mururu/8836338 to your computer and use it in GitHub Desktop.
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
Copy link

devinus commented Feb 6, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment