/deps/gen_smtp/src/binstr.erl

https://code.google.com/p/zotonic/ · Erlang · 329 lines · 257 code · 41 blank · 31 comment · 4 complexity · 6ad34b2526b8e43d8a497b28f6762bff MD5 · raw file

  1. %%% Copyright 2009 Andrew Thompson <andrew@hijacked.us>. All rights reserved.
  2. %%%
  3. %%% Redistribution and use in source and binary forms, with or without
  4. %%% modification, are permitted provided that the following conditions are met:
  5. %%%
  6. %%% 1. Redistributions of source code must retain the above copyright notice,
  7. %%% this list of conditions and the following disclaimer.
  8. %%% 2. Redistributions in binary form must reproduce the above copyright
  9. %%% notice, this list of conditions and the following disclaimer in the
  10. %%% documentation and/or other materials provided with the distribution.
  11. %%%
  12. %%% THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR
  13. %%% IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  14. %%% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  15. %%% EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  16. %%% INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  17. %%% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  18. %%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  19. %%% ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  20. %%% (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  21. %%% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. %% @doc Some functions for working with binary strings.
  23. -module(binstr).
  24. -export([
  25. strchr/2,
  26. strrchr/2,
  27. strpos/2,
  28. strrpos/2,
  29. substr/2,
  30. substr/3,
  31. split/3,
  32. split/2,
  33. chomp/1,
  34. strip/1,
  35. strip/2,
  36. strip/3,
  37. to_lower/1,
  38. to_upper/1,
  39. all/2,
  40. reverse/1,
  41. reverse_str_to_bin/1,
  42. join/2
  43. ]).
  44. -spec strchr(Bin :: binary(), C :: char()) -> non_neg_integer().
  45. strchr(Bin, C) when is_binary(Bin) ->
  46. % try to use the R14B binary module
  47. try binary:match(Bin, <<C>>) of
  48. {Index, _Length} ->
  49. Index + 1;
  50. nomatch ->
  51. 0
  52. catch
  53. _:_ ->
  54. strchr(Bin, C, 0)
  55. end.
  56. strchr(Bin, C, I) ->
  57. case Bin of
  58. <<_X:I/binary>> ->
  59. 0;
  60. <<_X:I/binary, C, _Rest/binary>> ->
  61. I+1;
  62. _ ->
  63. strchr(Bin, C, I+1)
  64. end.
  65. -spec strrchr(Bin :: binary(), C :: char()) -> non_neg_integer().
  66. strrchr(Bin, C) ->
  67. strrchr(Bin, C, byte_size(Bin)).
  68. strrchr(Bin, C, I) ->
  69. case Bin of
  70. <<_X:I/binary, C, _Rest/binary>> ->
  71. I+1;
  72. _ when I =< 1 ->
  73. 0;
  74. _ ->
  75. strrchr(Bin, C, I-1)
  76. end.
  77. -spec strpos(Bin :: binary(), C :: binary() | list()) -> non_neg_integer().
  78. strpos(Bin, C) when is_binary(Bin), is_list(C) ->
  79. strpos(Bin, list_to_binary(C));
  80. strpos(Bin, C) when is_binary(Bin) ->
  81. % try to use the R14B binary module
  82. try binary:match(Bin, C) of
  83. {Index, _Length} ->
  84. Index+1;
  85. nomatch ->
  86. 0
  87. catch
  88. _:_ ->
  89. strpos(Bin, C, 0, byte_size(C))
  90. end.
  91. strpos(Bin, C, I, S) ->
  92. case Bin of
  93. <<_X:I/binary>> ->
  94. 0;
  95. <<_X:I/binary, C:S/binary, _Rest/binary>> ->
  96. I+1;
  97. _ ->
  98. strpos(Bin, C, I+1, S)
  99. end.
  100. -spec strrpos(Bin :: binary(), C :: binary() | list()) -> non_neg_integer().
  101. strrpos(Bin, C) ->
  102. strrpos(Bin, C, byte_size(Bin), byte_size(C)).
  103. strrpos(Bin, C, I, S) ->
  104. case Bin of
  105. <<_X:I/binary, C:S/binary, _Rest/binary>> ->
  106. I+1;
  107. _ when I =< 1 ->
  108. 0;
  109. _ ->
  110. strrpos(Bin, C, I-1, S)
  111. end.
  112. -spec substr(Bin :: binary(), Start :: pos_integer() | neg_integer()) -> binary().
  113. substr(<<>>, _) ->
  114. <<>>;
  115. substr(Bin, Start) when Start > 0 ->
  116. {_, B2} = split_binary(Bin, Start-1),
  117. B2;
  118. substr(Bin, Start) when Start < 0 ->
  119. Size = byte_size(Bin),
  120. {_, B2} = split_binary(Bin, Size+Start),
  121. B2.
  122. -spec substr(Bin :: binary(), Start :: pos_integer() | neg_integer(), Length :: pos_integer()) -> binary().
  123. substr(<<>>, _, _) ->
  124. <<>>;
  125. substr(Bin, Start, Length) when Start > 0 ->
  126. {_, B2} = split_binary(Bin, Start-1),
  127. {B3, _} = split_binary(B2, Length),
  128. B3;
  129. substr(Bin, Start, Length) when Start < 0 ->
  130. Size = byte_size(Bin),
  131. {_, B2} = split_binary(Bin, Size+Start),
  132. {B3, _} = split_binary(B2, Length),
  133. B3.
  134. -spec split(Bin :: binary(), Separator :: binary(), SplitCount :: pos_integer()) -> [binary()].
  135. split(Bin, Separator, SplitCount) ->
  136. split_(Bin, Separator, SplitCount, []).
  137. split_(<<>>, _Separator, _SplitCount, Acc) ->
  138. lists:reverse(Acc);
  139. split_(Bin, <<>>, 1, Acc) ->
  140. lists:reverse([Bin | Acc]);
  141. split_(Bin, _Separator, 1, Acc) ->
  142. lists:reverse([Bin | Acc]);
  143. split_(Bin, <<>>, SplitCount, Acc) ->
  144. split_(substr(Bin, 2), <<>>, SplitCount - 1, [substr(Bin, 1, 1) | Acc]);
  145. split_(Bin, Separator, SplitCount, Acc) ->
  146. case strpos(Bin, Separator) of
  147. 0 ->
  148. lists:reverse([Bin | Acc]);
  149. Index ->
  150. Head = substr(Bin, 1, Index - 1),
  151. Tailpresplit = substr(Bin, Index + byte_size(Separator)),
  152. split_(Tailpresplit, Separator, SplitCount - 1, [Head | Acc])
  153. end.
  154. -spec split(Bin :: binary(), Separator :: binary()) -> [binary()].
  155. split(Bin, Separator) ->
  156. % try to use the R14B binary module
  157. try binary:split(Bin, Separator, [global]) of
  158. Result ->
  159. case lists:last(Result) of
  160. <<>> ->
  161. lists:sublist(Result, length(Result) - 1);
  162. _ ->
  163. Result
  164. end
  165. catch
  166. _:_ ->
  167. split_(Bin, Separator, [])
  168. end.
  169. split_(<<>>, _Separator, Acc) ->
  170. lists:reverse(Acc);
  171. split_(Bin, <<>>, Acc) ->
  172. split_(substr(Bin, 2), <<>>, [substr(Bin, 1, 1) | Acc]);
  173. split_(Bin, Separator, Acc) ->
  174. case strpos(Bin, Separator) of
  175. 0 ->
  176. lists:reverse([Bin | Acc]);
  177. Index ->
  178. split_(substr(Bin, Index + byte_size(Separator)), Separator, [substr(Bin, 1, Index - 1) | Acc])
  179. end.
  180. -spec chomp(Bin :: binary()) -> binary().
  181. chomp(Bin) ->
  182. L = byte_size(Bin),
  183. try [binary:at(Bin, L-2), binary:at(Bin, L-1)] of
  184. "\r\n" ->
  185. binary:part(Bin, 0, L-2);
  186. [_, X] when X == $\r; X == $\n ->
  187. binary:part(Bin, 0, L-1);
  188. _ ->
  189. Bin
  190. catch
  191. _:_ ->
  192. io:format("fallback, yay~n"),
  193. L2 = L - 1,
  194. case strrpos(Bin, <<"\r\n">>) of
  195. L2 ->
  196. substr(Bin, 1, L2 - 1);
  197. _ ->
  198. case strrchr(Bin, $\n) of
  199. L ->
  200. substr(Bin, 1, L - 1);
  201. _ ->
  202. case strrchr(Bin, $\r) of
  203. L ->
  204. substr(Bin, 1, L - 1);
  205. _ ->
  206. Bin
  207. end
  208. end
  209. end
  210. end.
  211. -spec strip(Bin :: binary()) -> binary().
  212. strip(Bin) ->
  213. strip(Bin, both, $\s).
  214. -spec strip(Bin :: binary(), Dir :: 'left' | 'right' | 'both') -> binary().
  215. strip(Bin, Dir) ->
  216. strip(Bin, Dir, $\s).
  217. -spec strip(Bin :: binary(), Dir :: 'left' | 'right' | 'both', C :: non_neg_integer()) -> binary().
  218. strip(<<>>, _, _) ->
  219. <<>>;
  220. strip(Bin, both, C) ->
  221. strip(strip(Bin, left, C), right, C);
  222. strip(<<C, _Rest/binary>> = Bin, left, C) ->
  223. strip(substr(Bin, 2), left, C);
  224. strip(Bin, left, _C) ->
  225. Bin;
  226. strip(Bin, right, C) ->
  227. L = byte_size(Bin),
  228. try binary:at(Bin, L-1) of
  229. C ->
  230. strip(binary:part(Bin, 0, L-1), right, C);
  231. _ ->
  232. Bin
  233. catch
  234. _:_ ->
  235. case strrchr(Bin, C) of
  236. L ->
  237. strip(substr(Bin, 1, L - 1), right, C);
  238. _ ->
  239. Bin
  240. end
  241. end.
  242. -spec to_lower(Bin :: binary()) -> binary().
  243. to_lower(Bin) ->
  244. to_lower(Bin, <<>>).
  245. to_lower(<<>>, Acc) ->
  246. Acc;
  247. to_lower(<<H, T/binary>>, Acc) when H >= $A, H =< $Z ->
  248. H2 = H + 32,
  249. to_lower(T, <<Acc/binary, H2>>);
  250. to_lower(<<H, T/binary>>, Acc) ->
  251. to_lower(T, <<Acc/binary, H>>).
  252. -spec to_upper(Bin :: binary()) -> binary().
  253. to_upper(Bin) ->
  254. to_upper(Bin, <<>>).
  255. to_upper(<<>>, Acc) ->
  256. Acc;
  257. to_upper(<<H, T/binary>>, Acc) when H >= $a, H =< $z ->
  258. H2 = H - 32,
  259. to_upper(T, <<Acc/binary, H2>>);
  260. to_upper(<<H, T/binary>>, Acc) ->
  261. to_upper(T, <<Acc/binary, H>>).
  262. -spec all(Fun :: function(), Binary :: binary()) -> boolean().
  263. all(_Fun, <<>>) ->
  264. true;
  265. all(Fun, Binary) ->
  266. Res = << <<X/integer>> || <<X>> <= Binary, Fun(X) >>,
  267. Binary == Res.
  268. %all(Fun, <<H, Tail/binary>>) ->
  269. % Fun(H) =:= true andalso all(Fun, Tail).
  270. %% this is a cool hack to very quickly reverse a binary
  271. -spec reverse(Bin :: binary()) -> binary().
  272. reverse(Bin) ->
  273. Size = byte_size(Bin)*8,
  274. <<T:Size/integer-little>> = Bin,
  275. <<T:Size/integer-big>>.
  276. %% reverse a string into a binary - can be faster than lists:reverse on large
  277. %% lists, even if you run binary_to_string on the result. For smaller strings
  278. %% it's probably slower (but still not that bad).
  279. -spec reverse_str_to_bin(String :: string()) -> binary().
  280. reverse_str_to_bin(String) ->
  281. reverse(list_to_binary(String)).
  282. -spec join(Binaries :: [binary()|list()], Glue :: binary() | list()) -> binary().
  283. join(Binaries, Glue) ->
  284. join(Binaries, Glue, []).
  285. join([H], _Glue, Acc) ->
  286. list_to_binary(lists:reverse([H | Acc]));
  287. join([H|T], Glue, Acc) ->
  288. join(T, Glue, [Glue, H | Acc]).