1<?php
2
3namespace Rubix\ML\Persisters\Serializers;
4
5use Rubix\ML\Encoding;
6use Rubix\ML\Persistable;
7use Rubix\ML\Exceptions\RuntimeException;
8use Rubix\ML\Exceptions\InvalidArgumentException;
9
10/**
11 * Gzip
12 *
13 * A compression format based on the DEFLATE algorithm with a header and CRC32 checksum.
14 *
15 * References:
16 * [1] P. Deutsch. (1996). RFC 1951 - DEFLATE Compressed Data Format Specification version.
17 *
18 * @category    Machine Learning
19 * @package     Rubix/ML
20 * @author      Andrew DalPino
21 */
22class Gzip implements Serializer
23{
24    /**
25     * The compression level between 0 and 9, 0 meaning no compression.
26     *
27     * @var int
28     */
29    protected $level;
30
31    /**
32     * The base serializer.
33     *
34     * @var \Rubix\ML\Persisters\Serializers\Serializer
35     */
36    protected $base;
37
38    /**
39     * @param int $level
40     * @param \Rubix\ML\Persisters\Serializers\Serializer|null $base
41     * @throws \Rubix\ML\Exceptions\InvalidArgumentException
42     */
43    public function __construct(int $level = 1, ?Serializer $base = null)
44    {
45        if ($level < 0 or $level > 9) {
46            throw new InvalidArgumentException('Level must be'
47                . " between 0 and 9, $level given.");
48        }
49
50        if ($base instanceof self) {
51            throw new InvalidArgumentException('Base serializer'
52                . ' must not be an instance of Gzip.');
53        }
54
55        $this->level = $level;
56        $this->base = $base ?? new Native();
57    }
58
59    /**
60     * Serialize a persistable object and return the data.
61     *
62     * @param \Rubix\ML\Persistable $persistable
63     * @return \Rubix\ML\Encoding
64     */
65    public function serialize(Persistable $persistable) : Encoding
66    {
67        $encoding = $this->base->serialize($persistable);
68
69        $data = gzencode($encoding, $this->level);
70
71        if ($data === false) {
72            throw new RuntimeException('Failed to compress data.');
73        }
74
75        return new Encoding($data);
76    }
77
78    /**
79     * Unserialize a persistable object and return it.
80     *
81     * @param \Rubix\ML\Encoding $encoding
82     * @throws \Rubix\ML\Exceptions\RuntimeException
83     * @return \Rubix\ML\Persistable
84     */
85    public function unserialize(Encoding $encoding) : Persistable
86    {
87        $data = gzdecode($encoding);
88
89        if ($data === false) {
90            throw new RuntimeException('Failed to decompress data.');
91        }
92
93        return $this->base->unserialize(new Encoding($data));
94    }
95
96    /**
97     * Return the string representation of the object.
98     *
99     * @return string
100     */
101    public function __toString() : string
102    {
103        return "Gzip (level: {$this->level}, base: {$this->base})";
104    }
105}
106