Created
February 6, 2014 00:40
-
-
Save mururu/8836338 to your computer and use it in GitHub Desktop.
StringIO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What's the logic behind https://gist.github.com/mururu/8836338#file-string_io-ex-L18?