Skip to content

Commit

Permalink
Only write compressed data when it's smaller
Browse files Browse the repository at this point in the history
  • Loading branch information
camelpunch committed Oct 20, 2024
1 parent 73f624f commit a1a679f
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 16 deletions.
9 changes: 9 additions & 0 deletions lib/mudbrick.ex
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ defmodule Mudbrick do
Enum.map_join(list, separator, &Mudbrick.Object.from/1)
end

def compress(data) do
z = :zlib.open()
:ok = :zlib.deflateInit(z)
deflated = :zlib.deflate(z, data, :finish)
:zlib.deflateEnd(z)
:zlib.close(z)
deflated
end

defp contents({doc, page}) do
import Document

Expand Down
41 changes: 25 additions & 16 deletions lib/mudbrick/stream.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,39 @@ defmodule Mudbrick.Stream do
length: nil,
filters: []

import :erlang, only: [iolist_size: 1]

def new(opts) do
opts =
if opts[:compress] do
opts
|> Keyword.update!(:data, fn data ->
z = :zlib.open()
:ok = :zlib.deflateInit(z)
deflated = :zlib.deflate(z, data, :finish)
:zlib.deflateEnd(z)
:zlib.close(z)
deflated
end)
|> then(fn opts ->
case decide_compression(opts) do
{:ok, data} ->
Keyword.merge(
opts,
length: :erlang.iolist_size(opts[:data]),
data: data,
length: iolist_size(data),
filters: [:FlateDecode]
)
end)
else
opts

:error ->
opts
end

struct!(__MODULE__, Keyword.put(opts, :length, :erlang.iolist_size(opts[:data])))
struct!(__MODULE__, Keyword.put(opts, :length, iolist_size(opts[:data])))
end

defp decide_compression(opts) do
if opts[:compress] do
uncompressed = opts[:data]
compressed = Mudbrick.compress(uncompressed)

if iolist_size(compressed) < iolist_size(uncompressed) do
{:ok, compressed}
else
:error
end
else
:error
end
end

defimpl Mudbrick.Object do
Expand Down
23 changes: 23 additions & 0 deletions test/mudbrick/stream_test.exs
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
defmodule Mudbrick.StreamTest do
use ExUnit.Case, async: true
use ExUnitProperties

import TestHelper

property "compresses data when there's a saving" do
check all uncompressed <- string(:alphanumeric, min_length: 150), max_runs: 200 do
result =
Mudbrick.Stream.new(compress: true, data: uncompressed)
|> Mudbrick.Object.from()
|> :erlang.iolist_to_binary()

assert result =~ "FlateDecode"
end
end

property "doesn't compress data when there's no saving" do
check all uncompressed <- string(:alphanumeric, max_length: 120), max_runs: 200 do
result =
Mudbrick.Stream.new(compress: true, data: uncompressed)
|> Mudbrick.Object.from()
|> :erlang.iolist_to_binary()

refute result =~ "FlateDecode"
end
end

test "includes length and stream markers when serialised" do
serialised =
Mudbrick.Stream.new(data: bodoni())
Expand Down

0 comments on commit a1a679f

Please sign in to comment.