Completed
Branch master (1c7ed8)
by Basarab
03:31 queued 01:55
created

FileTreePersister   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 3
dl 0
loc 170
ccs 74
cts 74
cp 1
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A convert() 0 21 1
B writeNode() 0 33 4
A writeCoordinate() 0 5 1
A initTree() 0 4 1
A calculateNodeSize() 0 4 1
A specifyNumberOfDimensions() 0 5 1
A specifyNumberOfItems() 0 6 1
A saveItemCoordinate() 0 8 2
A persistLeftLink() 0 8 1
A writeItemId() 0 6 1
1
<?php
2
3
namespace Hexogen\KDTree;
4
5
class FileTreePersister 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
        $this->writeNode($tree->getRoot());
56 3
        fclose($this->handler);
57 3
    }
58
59
    /**
60
     * @param NodeInterface $node
61
     */
62 3
    private function writeNode(NodeInterface $node)
63
    {
64 3
        $position = ftell($this->handler);
65 3
        $item = $node->getItem();
66
67 3
        $this->writeItemId($item);
68
69 3
        $dataChunk = pack('V', 0); // left position currently unknown so it equal 0/null
70 3
        fwrite($this->handler, $dataChunk);
71
72 3
        $rightNode = $node->getRight();
73
74 3
        $rightPosition = 0;
75 3
        if ($rightNode) {
76 3
            $rightPosition = $position + $this->nodeMemorySize;
77
        }
78 3
        $dataChunk = pack('V', $rightPosition);
79 3
        fwrite($this->handler, $dataChunk);
80
81 3
        $this->saveItemCoordinate($item);
82
83 3
        if ($rightNode) {
84 3
            $this->writeNode($rightNode);
85
        }
86
87 3
        $leftNode = $node->getLeft();
88
89 3
        if ($leftNode == null) {
90 3
            return;
91
        }
92 3
        $this->persistLeftLink($position);
93 3
        $this->writeNode($leftNode);
94 3
    }
95
96
    /**
97
     * @param array $coordinate
98
     */
99 3
    private function writeCoordinate(array $coordinate)
100
    {
101 3
        $dataChunk = pack('d'.$this->dimensions, ...$coordinate);
102 3
        fwrite($this->handler, $dataChunk);
103 3
    }
104
105
    /**
106
     * @param string $identifier
107
     */
108 3
    private function initTree(string $identifier)
109
    {
110 3
        $this->handler = fopen($this->path . '/' . $identifier, 'wb');
111 3
    }
112
113
    /**
114
     * Calculate memory size in file needed for single node
115
     */
116 3
    private function calculateNodeSize()
117
    {
118 3
        $this->nodeMemorySize = $this->dimensions * 8 + 3 * 4;
119 3
    }
120
121
    /**
122
     * Specify number of dimensions according to file format
123
     */
124 3
    private function specifyNumberOfDimensions()
125
    {
126 3
        $dataChunk = pack('V', $this->dimensions);
127 3
        fwrite($this->handler, $dataChunk);
128 3
    }
129
130
    /**
131
     * @param KDTreeInterface $tree
132
     */
133 3
    private function specifyNumberOfItems(KDTreeInterface $tree)
134
    {
135 3
        $itemCount = $tree->getItemCount();
136 3
        $dataChunk = pack('V', $itemCount);
137 3
        fwrite($this->handler, $dataChunk);
138 3
    }
139
140
    /**
141
     * @param $item
142
     */
143 3
    private function saveItemCoordinate(ItemInterface $item)
144
    {
145 3
        $coordinate = [];
146 3
        for ($i = 0; $i < $this->dimensions; $i++) {
147 3
            $coordinate[] = $item->getNthDimension($i);
148
        }
149 3
        $this->writeCoordinate($coordinate);
150 3
    }
151
152
    /**
153
     * Persist current position before writing left node
154
     * @param $position
155
     */
156 3
    private function persistLeftLink($position)
157
    {
158 3
        $leftPosition = ftell($this->handler);
159 3
        fseek($this->handler, $position + 4);
160 3
        $dataChunk = pack('V', $leftPosition);
161 3
        fwrite($this->handler, $dataChunk);
162 3
        fseek($this->handler, $leftPosition);
163 3
    }
164
165
    /**
166
     * @param $item
167
     */
168 3
    private function writeItemId(ItemInterface $item)
169
    {
170 3
        $itemId = $item->getId();
171 3
        $dataChunk = pack('V', $itemId);
172 3
        fwrite($this->handler, $dataChunk);
173 3
    }
174
}
175