Skip to content

Commit 1d42e7a

Browse files
committed
[0.2.x] Improve file read/write tests
1 parent ca5e06f commit 1d42e7a

File tree

4 files changed

+113
-48
lines changed

4 files changed

+113
-48
lines changed

src/Eio/File.php

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
final class File implements FileInterface
1212
{
13+
private const READ_CHUNK_FIZE = 65536;
14+
1315
use StatTrait;
1416

1517
private PollInterface $poll;
@@ -37,22 +39,29 @@ public function getContents(int $offset = 0 , ?int $maxlen = null): PromiseInter
3739
0,
3840
)->then(
3941
function ($fileDescriptor) use ($offset, $maxlen): PromiseInterface {
40-
if ($maxlen === null) {
41-
$sizePromise = $this->statFileDescriptor($fileDescriptor)->then(static function ($stat): int {
42-
return (int)$stat['size'];
43-
});
44-
} else {
45-
$sizePromise = resolve($maxlen);
46-
}
47-
return $sizePromise->then(function ($length) use ($fileDescriptor, $offset): PromiseInterface {
48-
return new Promise (function (callable $resolve) use ($fileDescriptor, $offset, $length): void {
49-
\eio_read($fileDescriptor, $length, $offset, \EIO_PRI_DEFAULT, function ($fileDescriptor, string $buffer) use ($resolve): void {
50-
$resolve($this->closeOpenFile($fileDescriptor)->then(function () use ($buffer): string {
51-
return $buffer;
52-
}));
42+
$buffer = '';
43+
$read = function () use ($fileDescriptor, $offset, $maxlen, &$read, &$buffer) {
44+
return new Promise (function (callable $resolve) use ($fileDescriptor, $offset, $maxlen, &$read, &$buffer): void {
45+
\eio_read($fileDescriptor, $maxlen ?? self::READ_CHUNK_FIZE, $offset, \PHP_INT_MAX, function ($fileDescriptor, string $contents) use ($resolve, $maxlen, &$read, &$buffer): void {
46+
$buffer .= $contents;
47+
$bufferLength = strlen($buffer);
48+
49+
if ($maxlen === null || $bufferLength >= $maxlen) {
50+
if ($maxlen !== null && $bufferLength > $maxlen) {
51+
$buffer = substr($buffer, 0, $maxlen);
52+
}
53+
54+
$resolve($this->closeOpenFile($fileDescriptor)->then(function () use ($buffer): string {
55+
return $buffer;
56+
}));
57+
} else {
58+
$read();
59+
}
5360
}, $fileDescriptor);
5461
});
55-
});
62+
};
63+
64+
return $read();
5665
}
5766
);
5867
}
@@ -77,15 +86,6 @@ function ($fileDescriptor) use ($contents, $flags): PromiseInterface {
7786
);
7887
}
7988

80-
private function statFileDescriptor($fileDescriptor): PromiseInterface
81-
{
82-
return new Promise(function (callable $resolve, callable $reject) use ($fileDescriptor) {
83-
\eio_fstat($fileDescriptor, \EIO_PRI_DEFAULT, function ($_, $stat) use ($resolve): void {
84-
$resolve($stat);
85-
}, $fileDescriptor);
86-
});
87-
}
88-
8989
private function openFile(string $path, int $flags, int $mode): PromiseInterface
9090
{
9191
return new Promise(function (callable $resolve, callable $reject) use ($path, $flags, $mode): void {

src/Fallback/File.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function stat(): PromiseInterface
2929
public function getContents(int $offset = 0 , ?int $maxlen = null): PromiseInterface
3030
{
3131
$path = $this->path . $this->name;
32-
return resolve(file_get_contents($path, false, null, $offset, $maxlen ?? (int)stat($path)['size']));
32+
return resolve(file_get_contents($path, false, null, $offset, $maxlen));
3333
}
3434

3535
public function putContents(string $contents, int $flags = 0): PromiseInterface

src/Uv/File.php

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
final class File implements FileInterface
1313
{
14+
private const READ_CHUNK_FIZE = 65536;
15+
1416
use StatTrait;
1517

1618
private ExtUvLoop $loop;
@@ -36,41 +38,88 @@ public function stat(): PromiseInterface
3638
public function getContents(int $offset = 0 , ?int $maxlen = null): PromiseInterface
3739
{
3840
$this->activate();
39-
return new Promise(function (callable $resolve) use ($offset, $maxlen): void {
40-
uv_fs_open($this->uvLoop, $this->path . DIRECTORY_SEPARATOR . $this->name, UV::O_RDONLY, 0, function ($fileDescriptor) use ($resolve, $offset, $maxlen): void {
41-
uv_fs_fstat($this->uvLoop, $fileDescriptor, function ($fileDescriptor, array $stat) use ($resolve, $offset, $maxlen): void {
42-
uv_fs_read($this->uvLoop, $fileDescriptor, $offset, $maxlen ?? (int)$stat['size'], function ($fileDescriptor, string $buffer) use ($resolve): void {
43-
$resolve($buffer);
44-
uv_fs_close($this->uvLoop, $fileDescriptor, function () {
45-
$this->deactivate();
41+
return $this->openFile(
42+
$this->path . DIRECTORY_SEPARATOR . $this->name,
43+
UV::O_RDONLY,
44+
0,
45+
)->then(
46+
function ($fileDescriptor) use ($offset, $maxlen): PromiseInterface {
47+
$buffer = '';
48+
$read = function () use ($fileDescriptor, $offset, $maxlen, &$read, &$buffer) {
49+
return new Promise (function (callable $resolve) use ($fileDescriptor, $offset, $maxlen, &$read, &$buffer): void {
50+
uv_fs_read($this->uvLoop, $fileDescriptor, $offset, $maxlen ?? self::READ_CHUNK_FIZE, function ($fileDescriptor, string $contents) use ($resolve, $maxlen, &$read, &$buffer): void {
51+
$buffer .= $contents;
52+
$bufferLength = strlen($buffer);
53+
54+
if ($maxlen === null || $bufferLength >= $maxlen) {
55+
if ($maxlen !== null && $bufferLength > $maxlen) {
56+
$buffer = substr($buffer, 0, $maxlen);
57+
}
58+
59+
$resolve($this->closeOpenFile($fileDescriptor)->then(function () use ($buffer): string {
60+
return $buffer;
61+
}));
62+
} else {
63+
$read();
64+
}
4665
});
4766
});
48-
});
49-
});
67+
};
68+
69+
return $read();
5070
});
5171
}
5272

5373
public function putContents(string $contents, int $flags = 0)
5474
{
5575
$this->activate();
56-
return new Promise(function (callable $resolve) use ($contents, $flags): void {
76+
return $this->openFile(
77+
$this->path . DIRECTORY_SEPARATOR . $this->name,
78+
(($flags & \FILE_APPEND) == \FILE_APPEND) ? UV::O_RDWR | UV::O_CREAT | UV::O_APPEND : UV::O_RDWR | UV::O_CREAT,
79+
0644,
80+
)->then(
81+
function ($fileDescriptor) use ($contents): PromiseInterface {
82+
return new Promise (function (callable $resolve) use ($contents, $fileDescriptor): void {
83+
uv_fs_write($this->uvLoop, $fileDescriptor, $contents, 0, function ($fileDescriptor, int $bytesWritten) use ($resolve): void {
84+
$resolve($this->closeOpenFile($fileDescriptor)->then(function () use ($bytesWritten): int {
85+
return $bytesWritten;
86+
}));
87+
});
88+
}
89+
);
90+
});
91+
}
92+
93+
private function openFile(string $path, int $flags, int $mode): PromiseInterface
94+
{
95+
return new Promise(function (callable $resolve) use ($path, $flags, $mode): void {
5796
uv_fs_open(
5897
$this->uvLoop,
5998
$this->path . DIRECTORY_SEPARATOR . $this->name,
60-
(($flags & \FILE_APPEND) == \FILE_APPEND) ? UV::O_RDWR | UV::O_CREAT | UV::O_APPEND : UV::O_RDWR | UV::O_CREAT,
61-
0644,
62-
function ($fileDescriptor) use ($resolve, $contents, $flags): void {
63-
uv_fs_write($this->uvLoop, $fileDescriptor, $contents, 0, function ($fileDescriptor, int $bytesWritten) use ($resolve): void {
64-
$resolve($bytesWritten);
65-
uv_fs_close($this->uvLoop, $fileDescriptor, function () {
66-
$this->deactivate();
67-
});
68-
});
99+
$flags,
100+
$mode,
101+
function ($fileDescriptor) use ($resolve): void {
102+
$resolve($fileDescriptor);
69103
}
70104
);
71105
});
72106
}
73107

108+
private function closeOpenFile($fileDescriptor): PromiseInterface
109+
{
110+
return new Promise(function (callable $resolve) use ($fileDescriptor) {
111+
try {
112+
uv_fs_close($this->uvLoop, $fileDescriptor, function () use ($resolve) {
113+
$this->deactivate();
114+
$resolve();
115+
});
116+
} catch (\Throwable $error) {
117+
$this->deactivate();
118+
throw $error;
119+
}
120+
});
121+
}
122+
74123
public function unlink(): PromiseInterface
75124
{
76125
$this->activate();

tests/FileTest.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ public function getContents(AdapterInterface $filesystem): void
3838
$fileContents = await($filesystem->detect(__FILE__)->then(static function (FileInterface $file): PromiseInterface {
3939
return $file->getContents();
4040
}));
41+
$blcokingReadFileContents = file_get_contents(__FILE__);
42+
43+
// self::assertSame(strlen($blcokingReadFileContents), strlen($fileContents));
44+
self::assertSame($blcokingReadFileContents, $fileContents);
45+
}
46+
47+
/**
48+
* @test
49+
*
50+
* @dataProvider provideFilesystems
51+
*/
52+
public function getContentsWithFilesize(AdapterInterface $filesystem): void
53+
{
54+
$fileContents = await($filesystem->detect(__FILE__)->then(static function (FileInterface $file): PromiseInterface {
55+
return $file->getContents(0, filesize(__FILE__));
56+
}));
4157

4258
self::assertSame(file_get_contents(__FILE__), $fileContents);
4359
}
@@ -95,7 +111,7 @@ public function putContentsMultipleBigFiles(AdapterInterface $filesystem): void
95111
$fileNames[] = $directoryName . bin2hex(random_bytes(13));
96112
}
97113
foreach ($fileNames as $fileName) {
98-
$fileContents[$fileName] = bin2hex(random_bytes(4096));
114+
$fileContents[$fileName] = bin2hex(random_bytes(4194304));
99115
touch($fileName);
100116
}
101117

@@ -155,7 +171,7 @@ public function putContentsAppendBigFile(AdapterInterface $filesystem): void
155171
$fileContents = [];
156172
$writtenLength = 0;
157173
for ($i = 0; $i < 13; $i++) {
158-
$fileContents[] = bin2hex(random_bytes(4096));
174+
$fileContents[] = bin2hex(random_bytes(4194304));
159175
}
160176

161177
foreach ($fileContents as $fileContent) {
@@ -177,7 +193,7 @@ public function putContentsAppendBigFile(AdapterInterface $filesystem): void
177193
*/
178194
public function putContentsAppendMultipleBigFiles(AdapterInterface $filesystem): void
179195
{
180-
$this->runMultipleFilesTests($filesystem, 8, 4096, 4);
196+
$this->runMultipleFilesTests($filesystem, 8, 4194304, 4);
181197
}
182198

183199
/**
@@ -187,7 +203,7 @@ public function putContentsAppendMultipleBigFiles(AdapterInterface $filesystem):
187203
*/
188204
public function putContentsAppendLotsOfSmallFiles(AdapterInterface $filesystem): void
189205
{
190-
$this->runMultipleFilesTests($filesystem, 16, 16, 4);
206+
$this->runMultipleFilesTests($filesystem, 16, 16384, 4);
191207
}
192208

193209
/**
@@ -197,7 +213,7 @@ public function putContentsAppendLotsOfSmallFiles(AdapterInterface $filesystem):
197213
*/
198214
public function putContentsAppendLoadsOfSmallFiles(AdapterInterface $filesystem): void
199215
{
200-
$this->runMultipleFilesTests($filesystem, 32, 8, 8);
216+
$this->runMultipleFilesTests($filesystem, 32, 8192, 8);
201217
}
202218

203219
public function runMultipleFilesTests(AdapterInterface $filesystem, int $fileCount, int $fileSize, int $chunkCount): void

0 commit comments

Comments
 (0)