Passed
Push — master ( 45b054...06b8db )
by Sam
02:37
created

ByteCodeWriter::writeBlock()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 10
ccs 8
cts 8
cp 1
crap 3
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace SPSS;
5
6
/**
7
 * Class ByteCodeWriter
8
 * Abstracts bytecode (de)compression
9
 * @package SPSS
10
 */
11
class ByteCodeWriter
12
{
13
    private const BYTE_SYS_MISSING = 255;
14
    private const BYTE_SPACES = 254;
15
    private const BYTE_LITERAL = 253;
16
    private const BYTE_NUL = 0;
17
18
    /** @var Buffer  */
19
    private $buffer;
20
21
    private $commandBuffer = [];
22
    private $dataBuffer = '';
23
    private $data = '';
24
25 8
    public function __construct(Buffer $buffer)
26
    {
27 8
        $this->buffer = $buffer;
28 8
    }
29
30
    /**
31
     * Appends data, data might not actually be written until flush is called
32
     */
33 7
    public function append(string $data): void
34
    {
35 7
        $data = $this->dataBuffer . $data;
36 7
        $this->dataBuffer = '';
37 7
        while(strlen($data) >= 8) {
38 7
            $block = substr($data, 0, 8);
39 7
            $data = substr($data, 8);
40
            switch ($block) {
41 7
                case pack('d', -PHP_FLOAT_MAX):
42
                    $this->commandBuffer[] = self::BYTE_SYS_MISSING;
43
                    break;
44 7
                case '        ':
45 4
                    $this->commandBuffer[] = self::BYTE_SPACES;
46 4
                    break;
47
                default:
48 6
                    $this->commandBuffer[] = self::BYTE_LITERAL;
49 6
                    $this->data .= $block;
50
            }
51 7
            $this->writeBlock();
52
        }
53 7
        $this->dataBuffer .= $data;
54 7
    }
55
56 7
    private function writeBlock(): void
57
    {
58 7
        if (count($this->commandBuffer) === 8) {
59 7
            $commandBlock = '';
60 7
            foreach ($this->commandBuffer as $cmd) {
61 7
                $commandBlock .= chr($cmd);
62
            }
63 7
            $this->buffer->write($commandBlock . $this->data);
64 7
            $this->data = '';
65 7
            $this->commandBuffer = [];
66
        }
67 7
    }
68
69
    /**
70
     * Fills the command buffer with nuls and writes the remaining data.
71
     */
72 7
    public function flush(): void
73
    {
74
        // Write remaining data by padding with spaces.
75 7
        if (!empty($this->dataBuffer)) {
76 2
            $this->append(str_repeat(' ', 8 - strlen($this->dataBuffer)));
77
        }
78 7
        if (!empty($this->commandBuffer)) {
79 7
            $this->commandBuffer = array_pad($this->commandBuffer, 8, self::BYTE_NUL);
80 7
            $this->writeBlock();
81
        }
82 7
        if (!empty($this->dataBuffer) || !empty($this->commandBuffer)) {
83
            throw new \Exception('Failed to flush one of the buffers');
84
        }
85 7
    }
86
87
}