Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
40 / 40 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
Huffman | |
100.00% |
40 / 40 |
|
100.00% |
4 / 4 |
21 | |
100.00% |
1 / 1 |
getDictionary | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setDictionary | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
encode | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
7 | |||
decode | |
100.00% |
23 / 23 |
|
100.00% |
1 / 1 |
12 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package phpOMS\Utils\Encoding\Huffman |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace phpOMS\Utils\Encoding\Huffman; |
16 | |
17 | /** |
18 | * Gray encoding class |
19 | * |
20 | * @package phpOMS\Utils\Encoding\Huffman |
21 | * @license OMS License 2.0 |
22 | * @link https://jingga.app |
23 | * @since 1.0.0 |
24 | */ |
25 | final class Huffman |
26 | { |
27 | /** |
28 | * Huffman dictionary. |
29 | * |
30 | * @var null|Dictionary |
31 | * @since 1.0.0 |
32 | */ |
33 | private ?Dictionary $dictionary = null; |
34 | |
35 | /** |
36 | * Get dictionary |
37 | * |
38 | * @return Dictionary |
39 | * |
40 | * @since 1.0.0 |
41 | */ |
42 | public function getDictionary() : ?Dictionary |
43 | { |
44 | return $this->dictionary; |
45 | } |
46 | |
47 | /** |
48 | * Set dictionary |
49 | * |
50 | * @param Dictionary $dictionary Huffman dictionary |
51 | * |
52 | * @return void |
53 | * |
54 | * @since 1.0.0 |
55 | */ |
56 | public function setDictionary(Dictionary $dictionary) : void |
57 | { |
58 | $this->dictionary = $dictionary; |
59 | } |
60 | |
61 | /** |
62 | * Encode. |
63 | * |
64 | * @param string $source Source to encode |
65 | * |
66 | * @return string |
67 | * |
68 | * @since 1.0.0 |
69 | */ |
70 | public function encode(string $source) : string |
71 | { |
72 | if (empty($source)) { |
73 | return ''; |
74 | } |
75 | |
76 | if (!isset($this->dictionary)) { |
77 | $this->dictionary = new Dictionary($source); |
78 | } |
79 | |
80 | $binary = ''; |
81 | for ($i = 0; isset($source[$i]); ++$i) { |
82 | $binary .= $this->dictionary->get($source[$i]); |
83 | } |
84 | |
85 | $splittedBinaryString = \str_split('1' . $binary . '1', 8); |
86 | $binary = ''; |
87 | |
88 | if ($splittedBinaryString === false) { |
89 | return $binary; // @codeCoverageIgnore |
90 | } |
91 | |
92 | foreach ($splittedBinaryString as $i => $c) { |
93 | while (\strlen($c) < 8) { |
94 | $c .= '0'; |
95 | } |
96 | |
97 | $binary .= \chr((int) \bindec($c)); |
98 | } |
99 | |
100 | return $binary; |
101 | } |
102 | |
103 | /** |
104 | * Decode. |
105 | * |
106 | * @param string $raw Raw to decode |
107 | * |
108 | * @return string |
109 | * |
110 | * @throws \Exception |
111 | * |
112 | * @since 1.0.0 |
113 | */ |
114 | public function decode(string $raw) : string |
115 | { |
116 | if (empty($raw) || $this->dictionary === null) { |
117 | return ''; |
118 | } |
119 | |
120 | $binary = ''; |
121 | $rawLenght = \strlen($raw); |
122 | $source = ''; |
123 | |
124 | for ($i = 0; $i < $rawLenght; ++$i) { |
125 | $decbin = \decbin(\ord($raw[$i])); |
126 | |
127 | while (\strlen($decbin) < 8) { |
128 | $decbin = '0' . $decbin; |
129 | } |
130 | |
131 | if ($i === 0) { |
132 | $pos = \strpos($decbin, '1'); |
133 | |
134 | if ($pos === false) { |
135 | throw new \Exception(); // @codeCoverageIgnore |
136 | } |
137 | |
138 | $decbin = \substr($decbin, $pos + 1); |
139 | if ($decbin === false) { |
140 | throw new \Exception(); // @codeCoverageIgnore |
141 | } |
142 | } |
143 | |
144 | if ($i + 1 === $rawLenght) { |
145 | $pos = \strrpos($decbin, '1'); |
146 | |
147 | if ($pos === false) { |
148 | throw new \Exception(); // @codeCoverageIgnore |
149 | } |
150 | |
151 | $decbin = \substr($decbin, 0, $pos); |
152 | if ($decbin === false) { |
153 | throw new \Exception(); // @codeCoverageIgnore |
154 | } |
155 | } |
156 | |
157 | $binary .= $decbin; |
158 | |
159 | while (($entry = $this->dictionary->getEntry($binary)) !== null) { |
160 | $source .= $entry; |
161 | } |
162 | } |
163 | |
164 | return $source; |
165 | } |
166 | } |