FSTreePersister::initTree()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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