Completed
Push — master ( ec6e3a...a32d5d )
by Thomas
10:38
created

AssemblyStreamZsync   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 194
Duplicated Lines 11.34 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 22
loc 194
rs 9.6
c 0
b 0
f 0
wmc 32
lcom 1
cbo 2

6 Methods

Rating   Name   Duplication   Size   Complexity  
B stream_open() 0 28 4
C stream_read() 7 57 11
B loadContext() 10 25 6
A wrap() 0 18 2
B getNextNodeStart() 2 9 5
A getNodeForPosition() 3 8 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/*
3
 * Copyright (C) by Ahmed Ammar <[email protected]>
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
 *
10
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
 * Software.
12
 *
13
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
 *
18
 */
19
namespace OCA\DAV\Upload;
20
21
use Sabre\DAV\IFile;
22
23
/**
24
 * Class AssemblyStreamZsync
25
 *
26
 * The assembly stream is a virtual stream that wraps multiple chunks.
27
 * Reading from the stream transparently accessed the underlying chunks and
28
 * give a representation as if they were already merged together.
29
 *
30
 * @package OCA\DAV\Upload
31
 */
32
class AssemblyStreamZsync extends AssemblyStream {
33
34
	/** @var array */
35
	private $backingFile = null;
36
37
	/** @var array */
38
	private $backingNode = null;
39
40
	/** @var array */
41
	private $currentNode = null;
42
43
	/** @var int */
44
	private $next = 0;
45
46
	/**
47
	 * @param string $path
48
	 * @param string $mode
49
	 * @param int $options
50
	 * @param string &$opened_path
51
	 * @return bool
52
	 */
53
	public function stream_open($path, $mode, $options, &$opened_path) {
54
		$this->loadContext('assembly');
55
56
		// sort the nodes
57
		$nodes = $this->nodes;
58
		// http://stackoverflow.com/a/10985500
59
		@usort($nodes, function(IFile $a, IFile $b) {
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
60
			return strnatcmp($a->getName(), $b->getName());
61
		});
62
		$this->nodes = $nodes;
63
64
		// build additional information
65
		$this->sortedNodes = [];
66
		foreach($this->nodes as $node) {
67
			$size = $node->getSize();
68
			$name = $node->getName();
69
			// ignore .zsync metadata file
70
			if (!strcmp($name,".zsync"))
71
				continue;
72
			if ($size == 0)
73
				continue;
74
			$this->sortedNodes[$name] = ['node' => $node, 'start' => (int)$name, 'end' => (int)$name + $size];
75
		}
76
77
		$this->backingNode = ["node" => $this->backingFile, "start" => 0, "end" => $this->backingFile->getSize()];
0 ignored issues
show
Bug introduced by
The method getSize cannot be called on $this->backingFile (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
78
		$this->currentNode = $this->backingNode;
79
		return true;
80
	}
81
82
	/**
83
	 * @param int $count
84
	 * @return string
85
	 */
86
	public function stream_read($count) {
87
		// we're done if we've reached the end of where we need to be
88
		if ($this->pos >= $this->size)
89
			return;
90
91
		// change the node/stream when we've reached the point we need to be at
92
		if ($this->currentStream === null || $this->pos == $this->next) {
93
			list($node, $posInNode) = $this->getNodeForPosition($this->pos);
94
			$this->currentStream = $this->getStream($node['node']);
95
			fseek($this->currentStream, $posInNode);
96
			$this->currentNode = $node;
97
		}
98
99
		// get the next byte offset when we need to change node/stream again
100
		if ($this->pos == $this->next)
101
			$this->next = $this->getNextNodeStart($this->pos);
102
103
		// catch case when client doesn't send enough data
104
		if ($this->next <= $this->pos) {
105
			$this->currentStream = null;
106
			$this->pos =$this->size;
107
			return;
108
		}
109
110
		// don't read beyond the expected file size
111
		if ($count + $this->pos >= $this->size)
112
			$count = $this->size - $this->pos;
113
114
		// don't read beyond the next marker
115
		if ($count + $this->pos >= $this->next)
116
			$count = $this->next - $this->pos;
117
118
		// read the data
119
		$data = fread($this->currentStream, $count);
120 View Code Duplication
		if (isset($data[$count - 1])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
			// we read the full count
122
			$read = $count;
123
		} else {
124
			// reaching end of stream, which happens less often so strlen is ok
125
			$read = strlen($data);
126
		}
127
128
		// update position
129
		$this->pos += $read;
130
131
		// if we couldn't read as much as expected we are done
132
		if ($read != $count) {
133
			$this->pos = $this->size;
134
		}
135
136
		// ensure we close the last stream or else we'll cause a locking issue
137
		if ($this->pos == $this->size) {
138
			$this->currentStream = null;
139
		}
140
141
		return $data;
142
	}
143
144
	/**
145
	 * Load the source from the stream context and return the context options
146
	 *
147
	 * @param string $name
148
	 * @return array
149
	 * @throws \Exception
150
	 */
151
	protected function loadContext($name) {
152
		$context = stream_context_get_options($this->context);
153 View Code Duplication
		if (isset($context[$name])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
154
			$context = $context[$name];
155
		} else {
156
			throw new \BadMethodCallException('Invalid context, "' . $name . '" options not set');
157
		}
158 View Code Duplication
		if (isset($context['nodes']) and is_array($context['nodes'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
			$this->nodes = $context['nodes'];
160
		} else {
161
			throw new \BadMethodCallException('Invalid context, nodes not set');
162
		}
163
		if (isset($context['backingFile'])) {
164
			$this->backingFile = $context['backingFile'];
165
		} else {
166
			throw new \BadMethodCallException('Invalid context, backingFile not set');
167
		}
168
		if (isset($context['fileLength'])) {
169
			$this->size = $context['fileLength'];
170
		} else {
171
			throw new \BadMethodCallException('Invalid context, fileLength not set');
172
		}
173
174
		return $context;
175
	}
176
177
	/**
178
	 * @param IFile[] $nodes
179
	 * @param IFile $backingFile
180
	 * @param $fileLength
181
	 * @return resource
182
	 *
183
	 * @throws \BadMethodCallException
184
	 */
185
	public static function wrap(array $nodes, IFile $backingFile = null, $fileLength = null) {
186
		$context = stream_context_create([
187
			'assembly' => [
188
				'nodes' => $nodes,
189
				'backingFile' => $backingFile,
190
				'fileLength' => $fileLength
191
			]
192
		]);
193
		stream_wrapper_register('assembly', '\OCA\DAV\Upload\AssemblyStreamZsync');
194
		try {
195
			$wrapped = fopen('assembly://', 'r', null, $context);
196
		} catch (\BadMethodCallException $e) {
197
			stream_wrapper_unregister('assembly');
198
			throw $e;
199
		}
200
		stream_wrapper_unregister('assembly');
201
		return $wrapped;
202
	}
203
204
	protected function getNextNodeStart($current) {
205
		foreach($this->sortedNodes as $node) {
206 View Code Duplication
			if ($current >= $node['start'] && $current < $node['end'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
207
				return $node['end'];
208
			if ($current < $node['start'])
209
				return $node['start'];
210
		}
211
		return $this->currentNode['end'];
212
	}
213
214
	/**
215
	 * @param $pos
216
	 */
217
	protected function getNodeForPosition($pos) {
218
		foreach($this->sortedNodes as $node) {
219 View Code Duplication
			if ($pos >= $node['start'] && $pos < $node['end']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
220
				return [$node, 0];
221
			}
222
		}
223
		return [$this->backingNode, $pos];
224
	}
225
}
226