%%% Copyright 2009 Andrew Thompson . All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions are met: %%% %%% 1. Redistributions of source code must retain the above copyright notice, %%% this list of conditions and the following disclaimer. %%% 2. Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in the %%% documentation and/or other materials provided with the distribution. %%% %%% THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR %%% IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF %%% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO %%% EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, %%% INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES %%% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; %%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND %%% ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT %%% (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS %%% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. %% @doc Some functions for working with binary strings. -module(binstr). -export([ strchr/2, strrchr/2, strpos/2, strrpos/2, substr/2, substr/3, split/3, split/2, chomp/1, strip/1, strip/2, strip/3, to_lower/1, to_upper/1, all/2, reverse/1, reverse_str_to_bin/1, join/2 ]). -spec strchr(Bin :: binary(), C :: char()) -> non_neg_integer(). strchr(Bin, C) when is_binary(Bin) -> case binary:match(Bin, <>) of {Index, _Length} -> Index + 1; nomatch -> 0 end. -spec strrchr(Bin :: binary(), C :: char()) -> non_neg_integer(). strrchr(Bin, C) -> strrchr(Bin, C, byte_size(Bin)). strrchr(Bin, C, I) -> case Bin of <<_X:I/binary, C, _Rest/binary>> -> I+1; _ when I =< 1 -> 0; _ -> strrchr(Bin, C, I-1) end. -spec strpos(Bin :: binary(), C :: binary() | list()) -> non_neg_integer(). strpos(Bin, C) when is_binary(Bin), is_list(C) -> strpos(Bin, list_to_binary(C)); strpos(Bin, C) when is_binary(Bin) -> case binary:match(Bin, C) of {Index, _Length} -> Index+1; nomatch -> 0 end. -spec strrpos(Bin :: binary(), C :: binary() | list()) -> non_neg_integer(). strrpos(Bin, C) -> strrpos(Bin, C, byte_size(Bin), byte_size(C)). strrpos(Bin, C, I, S) -> case Bin of <<_X:I/binary, C:S/binary, _Rest/binary>> -> I+1; _ when I =< 1 -> 0; _ -> strrpos(Bin, C, I-1, S) end. -spec substr(Bin :: binary(), Start :: pos_integer() | neg_integer()) -> binary(). substr(<<>>, _) -> <<>>; substr(Bin, Start) when Start > 0 -> {_, B2} = split_binary(Bin, Start-1), B2; substr(Bin, Start) when Start < 0 -> Size = byte_size(Bin), {_, B2} = split_binary(Bin, Size+Start), B2. -spec substr(Bin :: binary(), Start :: pos_integer() | neg_integer(), Length :: pos_integer()) -> binary(). substr(<<>>, _, _) -> <<>>; substr(Bin, Start, Length) when Start > 0 -> {_, B2} = split_binary(Bin, Start-1), {B3, _} = split_binary(B2, Length), B3; substr(Bin, Start, Length) when Start < 0 -> Size = byte_size(Bin), {_, B2} = split_binary(Bin, Size+Start), {B3, _} = split_binary(B2, Length), B3. -spec split(Bin :: binary(), Separator :: binary(), SplitCount :: pos_integer()) -> [binary()]. split(Bin, Separator, SplitCount) -> split_(Bin, Separator, SplitCount, []). split_(<<>>, _Separator, _SplitCount, Acc) -> lists:reverse(Acc); split_(Bin, <<>>, 1, Acc) -> lists:reverse([Bin | Acc]); split_(Bin, _Separator, 1, Acc) -> lists:reverse([Bin | Acc]); split_(Bin, <<>>, SplitCount, Acc) -> split_(substr(Bin, 2), <<>>, SplitCount - 1, [substr(Bin, 1, 1) | Acc]); split_(Bin, Separator, SplitCount, Acc) -> case strpos(Bin, Separator) of 0 -> lists:reverse([Bin | Acc]); Index -> Head = substr(Bin, 1, Index - 1), Tailpresplit = substr(Bin, Index + byte_size(Separator)), split_(Tailpresplit, Separator, SplitCount - 1, [Head | Acc]) end. -spec split(Bin :: binary(), Separator :: binary()) -> [binary()]. split(Bin, Separator) -> case binary:split(Bin, Separator, [global]) of Result -> case lists:last(Result) of <<>> -> lists:sublist(Result, length(Result) - 1); _ -> Result end end. -spec chomp(Bin :: binary()) -> binary(). chomp(Bin) -> L = byte_size(Bin), case [binary:at(Bin, L-2), binary:at(Bin, L-1)] of "\r\n" -> binary:part(Bin, 0, L-2); [_, X] when X == $\r; X == $\n -> binary:part(Bin, 0, L-1); _ -> Bin end. -spec strip(Bin :: binary()) -> binary(). strip(Bin) -> strip(Bin, both, $\s). -spec strip(Bin :: binary(), Dir :: 'left' | 'right' | 'both') -> binary(). strip(Bin, Dir) -> strip(Bin, Dir, $\s). -spec strip(Bin :: binary(), Dir :: 'left' | 'right' | 'both', C :: non_neg_integer()) -> binary(). strip(<<>>, _, _) -> <<>>; strip(Bin, both, C) -> strip(strip(Bin, left, C), right, C); strip(<> = Bin, left, C) -> strip(substr(Bin, 2), left, C); strip(Bin, left, _C) -> Bin; strip(Bin, right, C) -> L = byte_size(Bin), case binary:at(Bin, L-1) of C -> strip(binary:part(Bin, 0, L-1), right, C); _ -> Bin end. -spec to_lower(Bin :: binary()) -> binary(). to_lower(Bin) -> to_lower(Bin, <<>>). to_lower(<<>>, Acc) -> Acc; to_lower(<>, Acc) when H >= $A, H =< $Z -> H2 = H + 32, to_lower(T, <>); to_lower(<>, Acc) -> to_lower(T, <>). -spec to_upper(Bin :: binary()) -> binary(). to_upper(Bin) -> to_upper(Bin, <<>>). to_upper(<<>>, Acc) -> Acc; to_upper(<>, Acc) when H >= $a, H =< $z -> H2 = H - 32, to_upper(T, <>); to_upper(<>, Acc) -> to_upper(T, <>). -spec all(Fun :: function(), Binary :: binary()) -> boolean(). all(_Fun, <<>>) -> true; all(Fun, Binary) -> Res = << <> || <> <= Binary, Fun(X) >>, Binary == Res. %all(Fun, <>) -> % Fun(H) =:= true andalso all(Fun, Tail). %% this is a cool hack to very quickly reverse a binary -spec reverse(Bin :: binary()) -> binary(). reverse(Bin) -> Size = byte_size(Bin)*8, <> = Bin, <>. %% reverse a string into a binary - can be faster than lists:reverse on large %% lists, even if you run binary_to_string on the result. For smaller strings %% it's probably slower (but still not that bad). -spec reverse_str_to_bin(String :: string()) -> binary(). reverse_str_to_bin(String) -> reverse(list_to_binary(String)). -spec join(Binaries :: [binary()|list()], Glue :: binary() | list()) -> binary(). join(Binaries, Glue) -> join(Binaries, Glue, []). join([H], _Glue, Acc) -> list_to_binary(lists:reverse([H | Acc])); join([H|T], Glue, Acc) -> join(T, Glue, [Glue, H | Acc]); join([], _Glue, _Acc) -> <<"">>.