PluginProbe
Media Cloud Sync / 1.2.6
Media Cloud Sync v1.2.6
1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 All 35 releases
media-cloud-sync / includes / sdk / s3 / Aws / Api / Parser / DecodingEventStreamIterator.php

DecodingEventStreamIterator.php in Media Cloud Sync 1.2.6, at includes/sdk/s3/Aws/Api/Parser/DecodingEventStreamIterator.php

247 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Dudlewebs\WPMCS\s3\Aws\Api\Parser;
4
5 use Iterator;
6 use Dudlewebs\WPMCS\s3\Aws\Api\DateTimeResult;
7 use Dudlewebs\WPMCS\s3\GuzzleHttp\Psr7;
8 use Dudlewebs\WPMCS\s3\Psr\Http\Message\StreamInterface;
9 use Dudlewebs\WPMCS\s3\Aws\Api\Parser\Exception\ParserException;
10 /**
11 * @internal Implements a decoder for a binary encoded event stream that will
12 * decode, validate, and provide individual events from the stream.
13 */
14 class DecodingEventStreamIterator implements Iterator
15 {
16 const HEADERS = 'headers';
17 const PAYLOAD = 'payload';
18 const LENGTH_TOTAL = 'total_length';
19 const LENGTH_HEADERS = 'headers_length';
20 const CRC_PRELUDE = 'prelude_crc';
21 const BYTES_PRELUDE = 12;
22 const BYTES_TRAILING = 4;
23 private static $preludeFormat = [self::LENGTH_TOTAL => 'decodeUint32', self::LENGTH_HEADERS => 'decodeUint32', self::CRC_PRELUDE => 'decodeUint32'];
24 private static $lengthFormatMap = [1 => 'decodeUint8', 2 => 'decodeUint16', 4 => 'decodeUint32', 8 => 'decodeUint64'];
25 private static $headerTypeMap = [0 => 'decodeBooleanTrue', 1 => 'decodeBooleanFalse', 2 => 'decodeInt8', 3 => 'decodeInt16', 4 => 'decodeInt32', 5 => 'decodeInt64', 6 => 'decodeBytes', 7 => 'decodeString', 8 => 'decodeTimestamp', 9 => 'decodeUuid'];
26 /** @var StreamInterface Stream of eventstream shape to parse. */
27 private $stream;
28 /** @var array Currently parsed event. */
29 private $currentEvent;
30 /** @var int Current in-order event key. */
31 private $key;
32 /** @var resource|\HashContext CRC32 hash context for event validation */
33 private $hashContext;
34 /** @var int $currentPosition */
35 private $currentPosition;
36 /**
37 * DecodingEventStreamIterator constructor.
38 *
39 * @param StreamInterface $stream
40 */
41 public function __construct(StreamInterface $stream)
42 {
43 $this->stream = $stream;
44 $this->rewind();
45 }
46 private function parseHeaders($headerBytes)
47 {
48 $headers = [];
49 $bytesRead = 0;
50 while ($bytesRead < $headerBytes) {
51 list($key, $numBytes) = $this->decodeString(1);
52 $bytesRead += $numBytes;
53 list($type, $numBytes) = $this->decodeUint8();
54 $bytesRead += $numBytes;
55 $f = self::$headerTypeMap[$type];
56 list($value, $numBytes) = $this->{$f}();
57 $bytesRead += $numBytes;
58 if (isset($headers[$key])) {
59 throw new ParserException('Duplicate key in event headers.');
60 }
61 $headers[$key] = $value;
62 }
63 return [$headers, $bytesRead];
64 }
65 private function parsePrelude()
66 {
67 $prelude = [];
68 $bytesRead = 0;
69 $calculatedCrc = null;
70 foreach (self::$preludeFormat as $key => $decodeFunction) {
71 if ($key === self::CRC_PRELUDE) {
72 $hashCopy = \hash_copy($this->hashContext);
73 $calculatedCrc = \hash_final($this->hashContext, \true);
74 $this->hashContext = $hashCopy;
75 }
76 list($value, $numBytes) = $this->{$decodeFunction}();
77 $bytesRead += $numBytes;
78 $prelude[$key] = $value;
79 }
80 if (\unpack('N', $calculatedCrc)[1] !== $prelude[self::CRC_PRELUDE]) {
81 throw new ParserException('Prelude checksum mismatch.');
82 }
83 return [$prelude, $bytesRead];
84 }
85 private function parseEvent()
86 {
87 $event = [];
88 if ($this->stream->tell() < $this->stream->getSize()) {
89 $this->hashContext = \hash_init('crc32b');
90 $bytesLeft = $this->stream->getSize() - $this->stream->tell();
91 list($prelude, $numBytes) = $this->parsePrelude();
92 if ($prelude[self::LENGTH_TOTAL] > $bytesLeft) {
93 throw new ParserException('Message length too long.');
94 }
95 $bytesLeft -= $numBytes;
96 if ($prelude[self::LENGTH_HEADERS] > $bytesLeft) {
97 throw new ParserException('Headers length too long.');
98 }
99 list($event[self::HEADERS], $numBytes) = $this->parseHeaders($prelude[self::LENGTH_HEADERS]);
100 $event[self::PAYLOAD] = Psr7\Utils::streamFor($this->readAndHashBytes($prelude[self::LENGTH_TOTAL] - self::BYTES_PRELUDE - $numBytes - self::BYTES_TRAILING));
101 $calculatedCrc = \hash_final($this->hashContext, \true);
102 $messageCrc = $this->stream->read(4);
103 if ($calculatedCrc !== $messageCrc) {
104 throw new ParserException('Message checksum mismatch.');
105 }
106 }
107 return $event;
108 }
109 // Iterator Functionality
110 /**
111 * @return array
112 */
113 #[\ReturnTypeWillChange]
114 public function current()
115 {
116 return $this->currentEvent;
117 }
118 /**
119 * @return int
120 */
121 #[\ReturnTypeWillChange]
122 public function key()
123 {
124 return $this->key;
125 }
126 #[\ReturnTypeWillChange]
127 public function next()
128 {
129 $this->currentPosition = $this->stream->tell();
130 if ($this->valid()) {
131 $this->key++;
132 $this->currentEvent = $this->parseEvent();
133 }
134 }
135 #[\ReturnTypeWillChange]
136 public function rewind()
137 {
138 $this->stream->rewind();
139 $this->key = 0;
140 $this->currentPosition = 0;
141 $this->currentEvent = $this->parseEvent();
142 }
143 /**
144 * @return bool
145 */
146 #[\ReturnTypeWillChange]
147 public function valid()
148 {
149 return $this->currentPosition < $this->stream->getSize();
150 }
151 // Decoding Utilities
152 private function readAndHashBytes($num)
153 {
154 $bytes = $this->stream->read($num);
155 \hash_update($this->hashContext, $bytes);
156 return $bytes;
157 }
158 private function decodeBooleanTrue()
159 {
160 return [\true, 0];
161 }
162 private function decodeBooleanFalse()
163 {
164 return [\false, 0];
165 }
166 private function uintToInt($val, $size)
167 {
168 $signedCap = \pow(2, $size - 1);
169 if ($val > $signedCap) {
170 $val -= 2 * $signedCap;
171 }
172 return $val;
173 }
174 private function decodeInt8()
175 {
176 $val = (int) \unpack('C', $this->readAndHashBytes(1))[1];
177 return [$this->uintToInt($val, 8), 1];
178 }
179 private function decodeUint8()
180 {
181 return [\unpack('C', $this->readAndHashBytes(1))[1], 1];
182 }
183 private function decodeInt16()
184 {
185 $val = (int) \unpack('n', $this->readAndHashBytes(2))[1];
186 return [$this->uintToInt($val, 16), 2];
187 }
188 private function decodeUint16()
189 {
190 return [\unpack('n', $this->readAndHashBytes(2))[1], 2];
191 }
192 private function decodeInt32()
193 {
194 $val = (int) \unpack('N', $this->readAndHashBytes(4))[1];
195 return [$this->uintToInt($val, 32), 4];
196 }
197 private function decodeUint32()
198 {
199 return [\unpack('N', $this->readAndHashBytes(4))[1], 4];
200 }
201 private function decodeInt64()
202 {
203 $val = $this->unpackInt64($this->readAndHashBytes(8))[1];
204 return [$this->uintToInt($val, 64), 8];
205 }
206 private function decodeUint64()
207 {
208 return [$this->unpackInt64($this->readAndHashBytes(8))[1], 8];
209 }
210 private function unpackInt64($bytes)
211 {
212 if (\version_compare(\PHP_VERSION, '5.6.3', '<')) {
213 $d = \unpack('N2', $bytes);
214 return [1 => $d[1] << 32 | $d[2]];
215 }
216 return \unpack('J', $bytes);
217 }
218 private function decodeBytes($lengthBytes = 2)
219 {
220 if (!isset(self::$lengthFormatMap[$lengthBytes])) {
221 throw new ParserException('Undefined variable length format.');
222 }
223 $f = self::$lengthFormatMap[$lengthBytes];
224 list($len, $bytes) = $this->{$f}();
225 return [$this->readAndHashBytes($len), $len + $bytes];
226 }
227 private function decodeString($lengthBytes = 2)
228 {
229 if (!isset(self::$lengthFormatMap[$lengthBytes])) {
230 throw new ParserException('Undefined variable length format.');
231 }
232 $f = self::$lengthFormatMap[$lengthBytes];
233 list($len, $bytes) = $this->{$f}();
234 return [$this->readAndHashBytes($len), $len + $bytes];
235 }
236 private function decodeTimestamp()
237 {
238 list($val, $bytes) = $this->decodeInt64();
239 return [DateTimeResult::createFromFormat('U.u', $val / 1000), $bytes];
240 }
241 private function decodeUuid()
242 {
243 $val = \unpack('H32', $this->readAndHashBytes(16))[1];
244 return [\substr($val, 0, 8) . '-' . \substr($val, 8, 4) . '-' . \substr($val, 12, 4) . '-' . \substr($val, 16, 4) . '-' . \substr($val, 20, 12), 16];
245 }
246 }
247