Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

106 righe
3.1 KiB

  1. %% Copyright (c) 2017-Present Pivotal Software, Inc. All rights reserved.
  2. %%
  3. %% This package, Looking Glass, is double-licensed under the Mozilla
  4. %% Public License 1.1 ("MPL") and the Apache License version 2
  5. %% ("ASL"). For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL,
  6. %% please see LICENSE-APACHE2.
  7. %%
  8. %% This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
  9. %% either express or implied. See the LICENSE file for specific language governing
  10. %% rights and limitations of this software.
  11. %%
  12. %% If you have any questions regarding licensing, please contact us at
  13. %% info@rabbitmq.com.
  14. -module(lg_file_reader).
  15. -export([fold/3]).
  16. -export([foreach/2]).
  17. -export([open/1]).
  18. -export([read_event/1]).
  19. -export([close/1]).
  20. -record(state, {
  21. io_device :: file:io_device(),
  22. ctx :: lz4f:dctx(),
  23. buffer = <<>> :: binary(),
  24. offset = 0 :: non_neg_integer(),
  25. uncompressed_offset = 0 :: non_neg_integer()
  26. }).
  27. %% High level API.
  28. fold(Fun, Acc, Filename) ->
  29. {ok, IoDevice} = open(Filename),
  30. Ctx = lz4f:create_decompression_context(),
  31. Ret = fold1(#state{io_device=IoDevice, ctx=Ctx}, Fun, Acc),
  32. ok = close(IoDevice),
  33. Ret.
  34. fold1(State0, Fun, Acc0) ->
  35. case read_event(State0) of
  36. {ok, Event, State} ->
  37. Acc = Fun(Event, Acc0),
  38. fold1(State, Fun, Acc);
  39. eof ->
  40. {ok, Acc0};
  41. Error = {error, _, _} ->
  42. Error
  43. end.
  44. foreach(Fun, Filename) ->
  45. {ok, IoDevice} = open(Filename),
  46. Ctx = lz4f:create_decompression_context(),
  47. Ret = foreach1(#state{io_device=IoDevice, ctx=Ctx}, Fun),
  48. ok = close(IoDevice),
  49. Ret.
  50. foreach1(State0, Fun) ->
  51. case read_event(State0) of
  52. {ok, Event, State} ->
  53. _ = Fun(Event),
  54. foreach1(State, Fun);
  55. eof ->
  56. ok;
  57. Error = {error, _, _} ->
  58. Error
  59. end.
  60. %% Low level API.
  61. open(Filename) ->
  62. file:open(Filename, [read, binary]).
  63. read_event(State=#state{buffer=Buffer}) ->
  64. case Buffer of
  65. <<Size:32, Bin:Size/binary, Rest/bits>> ->
  66. convert_event_body(State#state{buffer=Rest}, Bin);
  67. _ ->
  68. read_file(State)
  69. end.
  70. read_file(State=#state{io_device=IoDevice, ctx=Ctx, buffer=Buffer, offset=Offset}) ->
  71. case file:read(IoDevice, 1000) of
  72. {ok, Data0} ->
  73. Data = iolist_to_binary(lz4f:decompress(Ctx, Data0)),
  74. read_event(State#state{buffer= <<Buffer/binary, Data/binary>>,
  75. offset=Offset + byte_size(Data0)});
  76. eof ->
  77. eof;
  78. {error, Reason} ->
  79. {error, Reason,
  80. 'An error occurred while trying to read from the file.'}
  81. end.
  82. convert_event_body(State=#state{offset=Offset, uncompressed_offset=UnOffset}, Bin) ->
  83. try binary_to_term(Bin) of
  84. Term ->
  85. {ok, Term, State#state{uncompressed_offset=UnOffset + byte_size(Bin)}}
  86. catch Class:Reason ->
  87. {error, {crash, Class, Reason, Offset, UnOffset},
  88. 'The binary form of an event could not be decoded to an Erlang term.'}
  89. end.
  90. close(IoDevice) ->
  91. file:close(IoDevice).