cowboy_iolists.erl 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. %% Copyright (c) 2017, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(cowboy_iolists).
  15. -export([split/2]).
  16. -spec split(non_neg_integer(), iodata()) -> {iodata(), iodata()}.
  17. split(N, Iolist) ->
  18. case split(N, Iolist, []) of
  19. {ok, Before, After} ->
  20. {Before, After};
  21. {more, _, Before} ->
  22. {lists:reverse(Before), <<>>}
  23. end.
  24. split(0, Rest, Acc) ->
  25. {ok, lists:reverse(Acc), Rest};
  26. split(N, [], Acc) ->
  27. {more, N, Acc};
  28. split(N, Binary, Acc) when byte_size(Binary) =< N ->
  29. {ok, lists:reverse([Binary|Acc]), <<>>};
  30. split(N, Binary, Acc) when is_binary(Binary) ->
  31. << Before:N/binary, After/bits >> = Binary,
  32. {ok, lists:reverse([Before|Acc]), After};
  33. split(N, [Binary|Tail], Acc) when byte_size(Binary) =< N ->
  34. split(N - byte_size(Binary), Tail, [Binary|Acc]);
  35. split(N, [Binary|Tail], Acc) when is_binary(Binary) ->
  36. << Before:N/binary, After/bits >> = Binary,
  37. {ok, lists:reverse([Before|Acc]), [After|Tail]};
  38. split(N, [Char|Tail], Acc) when is_integer(Char) ->
  39. split(N - 1, Tail, [Char|Acc]);
  40. split(N, [List|Tail], Acc0) ->
  41. case split(N, List, Acc0) of
  42. {ok, Before, After} ->
  43. {ok, Before, [After|Tail]};
  44. {more, More, Acc} ->
  45. split(More, Tail, Acc)
  46. end.
  47. -ifdef(TEST).
  48. split_test_() ->
  49. Tests = [
  50. {10, "Hello world!", "Hello worl", "d!"},
  51. {10, <<"Hello world!">>, "Hello worl", "d!"},
  52. {10, ["He", [<<"llo">>], $\s, [["world"], <<"!">>]], "Hello worl", "d!"},
  53. {10, ["Hello "|<<"world!">>], "Hello worl", "d!"},
  54. {10, "Hello!", "Hello!", ""},
  55. {10, <<"Hello!">>, "Hello!", ""},
  56. {10, ["He", [<<"ll">>], $o, [["!"]]], "Hello!", ""},
  57. {10, ["Hel"|<<"lo!">>], "Hello!", ""}
  58. ],
  59. [{iolist_to_binary(V), fun() ->
  60. {B, A} = split(N, V),
  61. true = iolist_to_binary(RB) =:= iolist_to_binary(B),
  62. true = iolist_to_binary(RA) =:= iolist_to_binary(A)
  63. end} || {N, V, RB, RA} <- Tests].
  64. -endif.