PluginProbe
Media Cloud Sync / 1.4.1
Media Cloud Sync v1.4.1
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.4.1, at includes/sdk/s3/Aws/Api/Parser/DecodingEventStreamIterator.php

254 lines 8.5 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 protected $stream;
28 /** @var array Currently parsed event. */
29 protected $currentEvent;
30 /** @var int Current in-order event key. */
31 protected $key;
32 /** @var resource|\HashContext CRC32 hash context for event validation */
33 protected $hashContext;
34 /** @var int $currentPosition */
35 protected $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 protected 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 protected 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 /**
86 * This method decodes an event from the stream.
87 *
88 * @return array
89 */
90 protected function parseEvent()
91 {
92 $event = [];
93 if ($this->stream->tell() < $this->stream->getSize()) {
94 $this->hashContext = \hash_init('crc32b');
95 $bytesLeft = $this->stream->getSize() - $this->stream->tell();
96 list($prelude, $numBytes) = $this->parsePrelude();
97 if ($prelude[self::LENGTH_TOTAL] > $bytesLeft) {
98 throw new ParserException('Message length too long.');
99 }
100 $bytesLeft -= $numBytes;
101 if ($prelude[self::LENGTH_HEADERS] > $bytesLeft) {
102 throw new ParserException('Headers length too long.');
103 }
104 list($event[self::HEADERS], $numBytes) = $this->parseHeaders($prelude[self::LENGTH_HEADERS]);
105 $event[self::PAYLOAD] = Psr7\Utils::streamFor($this->readAndHashBytes($prelude[self::LENGTH_TOTAL] - self::BYTES_PRELUDE - $numBytes - self::BYTES_TRAILING));
106 $calculatedCrc = \hash_final($this->hashContext, \true);
107 $messageCrc = $this->stream->read(4);
108 if ($calculatedCrc !== $messageCrc) {
109 throw new ParserException('Message checksum mismatch.');
110 }
111 }
112 return $event;
113 }
114 // Iterator Functionality
115 /**
116 * @return array
117 */
118 #[\ReturnTypeWillChange]
119 public function current()
120 {
121 return $this->currentEvent;
122 }
123 /**
124 * @return int
125 */
126 #[\ReturnTypeWillChange]
127 public function key()
128 {
129 return $this->key;
130 }
131 /**
132 * @return void
133 */
134 #[\ReturnTypeWillChange]
135 public function next()
136 {
137 $this->currentPosition = $this->stream->tell();
138 if ($this->valid()) {
139 $this->key++;
140 $this->currentEvent = $this->parseEvent();
141 }
142 }
143 /**
144 * @return void
145 */
146 #[\ReturnTypeWillChange]
147 public function rewind()
148 {
149 $this->stream->rewind();
150 $this->key = 0;
151 $this->currentPosition = 0;
152 $this->currentEvent = $this->parseEvent();
153 }
154 /**
155 * @return bool
156 */
157 #[\ReturnTypeWillChange]
158 public function valid()
159 {
160 return $this->currentPosition < $this->stream->getSize();
161 }
162 // Decoding Utilities
163 protected function readAndHashBytes($num)
164 {
165 $bytes = $this->stream->read($num);
166 \hash_update($this->hashContext, $bytes);
167 return $bytes;
168 }
169 private function decodeBooleanTrue()
170 {
171 return [\true, 0];
172 }
173 private function decodeBooleanFalse()
174 {
175 return [\false, 0];
176 }
177 private function uintToInt($val, $size)
178 {
179 $signedCap = \pow(2, $size - 1);
180 if ($val > $signedCap) {
181 $val -= 2 * $signedCap;
182 }
183 return $val;
184 }
185 private function decodeInt8()
186 {
187 $val = (int) \unpack('C', $this->readAndHashBytes(1))[1];
188 return [$this->uintToInt($val, 8), 1];
189 }
190 private function decodeUint8()
191 {
192 return [\unpack('C', $this->readAndHashBytes(1))[1], 1];
193 }
194 private function decodeInt16()
195 {
196 $val = (int) \unpack('n', $this->readAndHashBytes(2))[1];
197 return [$this->uintToInt($val, 16), 2];
198 }
199 private function decodeUint16()
200 {
201 return [\unpack('n', $this->readAndHashBytes(2))[1], 2];
202 }
203 private function decodeInt32()
204 {
205 $val = (int) \unpack('N', $this->readAndHashBytes(4))[1];
206 return [$this->uintToInt($val, 32), 4];
207 }
208 private function decodeUint32()
209 {
210 return [\unpack('N', $this->readAndHashBytes(4))[1], 4];
211 }
212 private function decodeInt64()
213 {
214 $val = $this->unpackInt64($this->readAndHashBytes(8))[1];
215 return [$this->uintToInt($val, 64), 8];
216 }
217 private function decodeUint64()
218 {
219 return [$this->unpackInt64($this->readAndHashBytes(8))[1], 8];
220 }
221 private function unpackInt64($bytes)
222 {
223 return \unpack('J', $bytes);
224 }
225 private function decodeBytes($lengthBytes = 2)
226 {
227 if (!isset(self::$lengthFormatMap[$lengthBytes])) {
228 throw new ParserException('Undefined variable length format.');
229 }
230 $f = self::$lengthFormatMap[$lengthBytes];
231 list($len, $bytes) = $this->{$f}();
232 return [$this->readAndHashBytes($len), $len + $bytes];
233 }
234 private function decodeString($lengthBytes = 2)
235 {
236 if (!isset(self::$lengthFormatMap[$lengthBytes])) {
237 throw new ParserException('Undefined variable length format.');
238 }
239 $f = self::$lengthFormatMap[$lengthBytes];
240 list($len, $bytes) = $this->{$f}();
241 return [$this->readAndHashBytes($len), $len + $bytes];
242 }
243 private function decodeTimestamp()
244 {
245 list($val, $bytes) = $this->decodeInt64();
246 return [DateTimeResult::createFromFormat('U.u', $val / 1000), $bytes];
247 }
248 private function decodeUuid()
249 {
250 $val = \unpack('H32', $this->readAndHashBytes(16))[1];
251 return [\substr($val, 0, 8) . '-' . \substr($val, 8, 4) . '-' . \substr($val, 12, 4) . '-' . \substr($val, 16, 4) . '-' . \substr($val, 20, 12), 16];
252 }
253 }
254