Current section
Files
Jump to
Current section
Files
lib/stream_gzip.ex
defmodule StreamGzip do
@moduledoc """
Gzip or gunzip a stream.
"""
@doc """
Gunzip the stream.
iex> [<<31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 171, 168, 172, 170, 168, 172, 2, 0, 60, 143, 60, 178, 6, 0, 0, 0>>]
iex> |> StreamGzip.gunzip
iex> |> Enum.into("")
"xyzxyz"
"""
@spec gunzip(Enumerable.t) :: Enumerable.t
def gunzip(enum) do
Stream.transform(
enum,
fn ->
z = :zlib.open
:zlib.inflateInit z, 16 + 15
z
end,
fn compressed, z ->
enum = Stream.resource(
fn -> :zlib.inflateChunk z, compressed end,
fn
:halt -> {:halt, nil}
{:more, decompressed} -> {List.wrap(decompressed), :zlib.inflateChunk(z)}
decompressed -> {List.wrap(decompressed), :halt}
end,
&(&1)
)
{enum, z}
end,
fn z ->
:zlib.inflateEnd z
:zlib.close z
end
)
end
@doc """
Gzip the stream.
iex> ["xyzxyz"] |> StreamGzip.gzip |> Enum.into("")
<<31, 139, 8, 0, 0, 0, 0, 0, 0, 3, 171, 168, 172, 170, 168, 172, 2, 0, 60, 143, 60, 178, 6, 0, 0, 0>>
"""
@spec gzip(Enumerable.t) :: Enumerable.t
def gzip(enum) do
z = :zlib.open
:zlib.deflateInit z, :default, :deflated, 16 + 15, 8, :default
transform_with_final enum, z, &{:zlib.deflate(&2, &1), &2}, fn z ->
iolist = :zlib.deflate z, "", :finish
:zlib.deflateEnd z
:zlib.close z
{iolist, z}
end
end
@spec transform_with_final(Enumerable.t, acc, fun, final_fun) :: Enumerable.t when
fun: (Stream.element, acc -> {Enumerable.t, acc} | {:halt, acc}),
final_fun: (acc -> {Enumerable.t, acc} | {:halt, acc}),
acc: any
defp transform_with_final(enum, acc, reducer, final_fun) do
ref = make_ref()
enum
|> Stream.concat([ref])
|> Stream.transform(acc, fn
^ref, acc -> final_fun.(acc)
element, acc -> reducer.(element, acc)
end)
end
end