Completed
Push — master ( f9fb84...12a14e )
by Basarab
05:55
created

FSTreePersister::specifyNumberOfItems()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Hexogen\KDTree;
4
5
class FSTreePersister implements TreePersisterInterface
6
{
7
    /**
8
     * @var string path to the file
9
     */
10
    private $path;
11
12
    /**
13
     * @var resource file handler
14
     */
15
    private $handler;
16
17
    /**
18
     * @var int
19
     */
20
    private $dimensions;
21
22
    /**
23
     * @var int
24
     */
25
    private $nodeMemorySize;
26
27 3
    public function __construct(string $path)
28
    {
29 3
        $this->path = $path;
30 3
    }
31
32
    /**
33
     * @param KDTreeInterface $tree
34
     * @param string $identifier that identifies persisted tree(may be a filename, database name etc.)
35
     * @return mixed
36
     */
37 3
    public function convert(KDTreeInterface $tree, string $identifier)
38
    {
39 3
        $this->initTree($identifier);
40
41 3
        $this->dimensions = $tree->getDimensionCount();
42
43 3
        $this->calculateNodeSize();
44
45 3
        $this->specifyNumberOfDimensions();
46
47 3
        $this->specifyNumberOfItems($tree);
48
49 3
        $upperBound = $tree->getMaxBoundary();
50 3
        $this->writeCoordinate($upperBound);
51
52 3
        $lowerBound = $tree->getMinBoundary();
53 3
        $this->writeCoordinate($lowerBound);
54
55 3
        $root  = $tree->getRoot();
56 3
        if ($root) {
57
            $this->writeNode($root);
58
        }
59 3
        fclose($this->handler);
60 3
    }
61
62
    /**
63
     * @param NodeInterface $node
64
     */
65
    private function writeNode(NodeInterface $node)
66
    {
67
        $position = ftell($this->handler);
68
        $item = $node->getItem();
69
70
        $this->writeItemId($item);
71
72
        $dataChunk = pack('V', 0); // left position currently unknown so it equal 0/null
73
        fwrite($this->handler, $dataChunk);
74
75
        $rightNode = $node->getRight();
76
77
        $rightPosition = 0;
78
        if ($rightNode) {
79
            $rightPosition = $position + $this->nodeMemorySize;
80
        }
81
        $dataChunk = pack('V', $rightPosition);
82
        fwrite($this->handler, $dataChunk);
83
84
        $this->saveItemCoordinate($item);
85
86
        if ($rightNode) {
87
            $this->writeNode($rightNode);
88
        }
89
90
        $leftNode = $node->getLeft();
91
92
        if ($leftNode == null) {
93
            return;
94
        }
95
        $this->persistLeftLink($position);
96
        $this->writeNode($leftNode);
97
    }
98
99
    /**
100
     * @param array $coordinate
101
     */
102 3
    private function writeCoordinate(array $coordinate)
103
    {
104 3
        $dataChunk = pack('d'.$this->dimensions, ...$coordinate);
105 3
        fwrite($this->handler, $dataChunk);
106 3
    }
107
108
    /**
109
     * @param string $identifier
110
     */
111 3
    private function initTree(string $identifier)
112
    {
113 3
        $this->handler = fopen($this->path . '/' . $identifier, 'wb');
114 3
    }
115
116
    /**
117
     * Calculate memory size in file needed for single node
118
     */
119 3
    private function calculateNodeSize()
120
    {
121 3
        $this->nodeMemorySize = $this->dimensions * 8 + 3 * 4;
122 3
    }
123
124
    /**
125
     * Specify number of dimensions according to file format
126
     */
127 3
    private function specifyNumberOfDimensions()
128
    {
129 3
        $dataChunk = pack('V', $this->dimensions);
130 3
        fwrite($this->handler, $dataChunk);
131 3
    }
132
133
    /**
134
     * @param KDTreeInterface $tree
135
     */
136 3
    private function specifyNumberOfItems(KDTreeInterface $tree)
137
    {
138 3
        $itemCount = $tree->getItemCount();
139 3
        $dataChunk = pack('V', $itemCount);
140 3
        fwrite($this->handler, $dataChunk);
141 3
    }
142
143
    /**
144
     * @param $item
145
     */
146
    private function saveItemCoordinate(ItemInterface $item)
147
    {
148
        $coordinate = [];
149
        for ($i = 0; $i < $this->dimensions; $i++) {
150
            $coordinate[] = $item->getNthDimension($i);
151
        }
152
        $this->writeCoordinate($coordinate);
153
    }
154
155
    /**
156
     * Persist current position before writing left node
157
     * @param $position
158
     */
159
    private function persistLeftLink($position)
160
    {
161
        $leftPosition = ftell($this->handler);
162
        fseek($this->handler, $position + 4);
163
        $dataChunk = pack('V', $leftPosition);
164
        fwrite($this->handler, $dataChunk);
165
        fseek($this->handler, $leftPosition);
166
    }
167
168
    /**
169
     * @param $item
170
     */
171
    private function writeItemId(ItemInterface $item)
172
    {
173
        $itemId = $item->getId();
174
        $dataChunk = pack('V', $itemId);
175
        fwrite($this->handler, $dataChunk);
176
    }
177
}
178