Skip to content

Commit 65e44af

Browse files
committed
Refactor Thrift reader
1 parent d9f81d4 commit 65e44af

File tree

2 files changed

+70
-73
lines changed

2 files changed

+70
-73
lines changed

src/Thrift/Compact/ReadBuffer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function rewind(): void
3232

3333
public function isEmpty(): bool
3434
{
35-
return $this->position < $this->length;
35+
return $this->position >= $this->length;
3636
}
3737

3838
public function readSignedByte(): int

src/Thrift/Compact/Reader.php

Lines changed: 69 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Fbns\Thrift\Map;
99
use Fbns\Thrift\Series;
1010
use Fbns\Thrift\Struct;
11-
use SplStack;
1211

1312
/**
1413
* WARNING: This implementation is not complete.
@@ -17,116 +16,121 @@
1716
*/
1817
class Reader
1918
{
20-
/** @var SplStack */
21-
private $stack;
22-
23-
/** @var int */
24-
private $field;
25-
2619
/** @var ReadBuffer */
2720
private $buffer;
2821

29-
public function __construct(string $buffer = '')
22+
public function __construct(string $buffer)
3023
{
31-
$this->stack = new SplStack();
3224
$this->buffer = new ReadBuffer($buffer);
3325
}
3426

3527
public function __invoke(): Struct
3628
{
3729
$this->buffer->rewind();
38-
$this->field = 0;
39-
while (!$this->stack->isEmpty()) {
40-
$this->stack->pop();
41-
}
4230

43-
return new Struct($this->readStruct());
31+
return $this->readStruct();
32+
}
33+
34+
private function readStruct(): Struct
35+
{
36+
return new Struct($this->readStructFields());
4437
}
4538

46-
private function readStruct(): \Generator
39+
private function readStructFields(): \Generator
4740
{
48-
while ($this->buffer->isEmpty()) {
49-
$type = $this->readField();
41+
$field = 0;
42+
while (!$this->buffer->isEmpty()) {
43+
[$field, $type] = $this->readNextField($field);
5044
switch ($type) {
51-
case Types::STRUCT:
52-
$field = $this->field;
53-
$this->stack->push($this->field);
54-
$this->field = 0;
55-
yield $field => new Struct($this->readStruct());
56-
break;
5745
case Types::STOP:
58-
if (!$this->stack->isEmpty()) {
59-
$this->field = $this->stack->pop();
60-
}
61-
6246
return;
63-
case Types::LIST:
64-
$sizeAndType = $this->buffer->readUnsignedByte();
65-
$size = $sizeAndType >> 4;
66-
$listType = $sizeAndType & 0x0f;
67-
if ($size === 0x0f) {
68-
$size = $this->buffer->readVarint();
69-
}
70-
yield $this->field => new Series($listType, $this->readList($size, $listType));
71-
break;
7247
case Types::TRUE:
7348
case Types::FALSE:
74-
yield $this->field => new Field($type, $type === Types::TRUE);
75-
break;
76-
case Types::BYTE:
77-
yield $this->field => new Field($type, $this->buffer->readSignedByte());
78-
break;
79-
case Types::I16:
80-
case Types::I32:
81-
case Types::I64:
82-
yield $this->field => new Field($type, $this->fromZigZag($this->buffer->readVarint()));
83-
break;
84-
case Types::BINARY:
85-
yield $this->field => new Field($type, $this->buffer->readString($this->buffer->readVarint()));
86-
break;
87-
case Types::MAP:
88-
$size = $this->buffer->readVarint();
89-
$types = $this->buffer->readUnsignedByte();
90-
$keyType = $types >> 4;
91-
$valueType = $types & 0x0f;
92-
yield $this->field => new Map($keyType, $valueType, $this->readMap($size, $keyType, $valueType));
49+
// Boolean fields are inlined, so there is no need to read them.
50+
yield $field => new Field(Types::TRUE, $type === Types::TRUE);
9351
break;
9452
default:
95-
throw new \DomainException("Unsupported field type {$type}.");
53+
yield $field => $this->readWrappedValue($type);
9654
}
9755
}
9856
}
9957

100-
private function readField(): int
58+
private function readNextField(int $currentField): array
10159
{
10260
$typeAndDelta = $this->buffer->readUnsignedByte();
10361
if ($typeAndDelta === Types::STOP) {
104-
return Types::STOP;
62+
return [$currentField, Types::STOP];
10563
}
10664
$delta = $typeAndDelta >> 4;
65+
$nextField = $currentField;
10766
if ($delta === 0) {
108-
$this->field = $this->fromZigZag($this->buffer->readVarint());
67+
$nextField = $this->fromZigZag($this->buffer->readVarint());
10968
} else {
110-
$this->field += $delta;
69+
$nextField += $delta;
11170
}
11271
$type = $typeAndDelta & 0x0f;
11372

114-
return $type;
73+
return [$nextField, $type];
11574
}
11675

117-
private function readList(int $size, int $type): \Generator
76+
private function readList(): Series
77+
{
78+
$sizeAndType = $this->buffer->readUnsignedByte();
79+
$size = $sizeAndType >> 4;
80+
$listType = $sizeAndType & 0x0f;
81+
if ($size === 0x0f) {
82+
$size = $this->buffer->readVarint();
83+
}
84+
85+
return new Series($listType, $this->readListItems($size, $listType));
86+
}
87+
88+
private function readListItems(int $size, int $type): \Generator
11889
{
11990
for ($i = 0; $i < $size; $i++) {
120-
yield $this->readPrimitive($type);
91+
yield $this->readValue($type);
12192
}
12293
}
12394

95+
private function readMap(): Map
96+
{
97+
$size = $this->buffer->readVarint();
98+
$types = $this->buffer->readUnsignedByte();
99+
$keyType = $types >> 4;
100+
$valueType = $types & 0x0f;
101+
102+
return new Map($keyType, $valueType, $this->readMapItems($size, $keyType, $valueType));
103+
}
104+
105+
private function readMapItems(int $size, int $keyType, int $valueType): \Generator
106+
{
107+
for ($i = 0; $i < $size; $i++) {
108+
yield $this->readValue($keyType) => $this->readValue($valueType);
109+
}
110+
}
111+
112+
private function readWrappedValue(int $type): Field
113+
{
114+
$result = $this->readValue($type);
115+
if ($result instanceof Field) {
116+
return $result;
117+
}
118+
119+
return new Field($type, $result);
120+
}
121+
124122
/**
125123
* @return mixed
126124
*/
127-
private function readPrimitive(int $type)
125+
private function readValue(int $type)
128126
{
129127
switch ($type) {
128+
case Types::STRUCT:
129+
return $this->readStruct();
130+
case Types::LIST:
131+
return $this->readList();
132+
case Types::MAP:
133+
return $this->readMap();
130134
case Types::TRUE:
131135
case Types::FALSE:
132136
return $this->buffer->readSignedByte() === Types::TRUE;
@@ -139,14 +143,7 @@ private function readPrimitive(int $type)
139143
case Types::BINARY:
140144
return $this->buffer->readString($this->buffer->readVarint());
141145
default:
142-
throw new \DomainException("Unsupported primitive type {$type}.");
143-
}
144-
}
145-
146-
private function readMap(int $size, int $keyType, int $valueType): \Generator
147-
{
148-
for ($i = 0; $i < $size; $i++) {
149-
yield $this->readPrimitive($keyType) => $this->readPrimitive($valueType);
146+
throw new \DomainException("Unsupported type {$type}.");
150147
}
151148
}
152149

0 commit comments

Comments
 (0)