Skip to content

Commit f32125a

Browse files
committed
References: Add tests, get back some performance.
1 parent be5ca6e commit f32125a

File tree

9 files changed

+104
-86
lines changed

9 files changed

+104
-86
lines changed

PhpSerializerNET.Test/Deserialize/DeserializeStructs.cs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,7 @@ public void DeserializeArrayToStruct() {
1818
Assert.Equal("Foo", value.foo);
1919
Assert.Equal("Bar", value.bar);
2020
}
21-
[Fact]
22-
public void DeserializeArrayToStructWithReference() {
23-
var value = PhpSerialization.Deserialize<AStruct>(
24-
"a:2:{s:3:\"foo\";s:3:\"Foo\";s:3:\"bar\";R:2;}"
25-
);
26-
Assert.Equal("Foo", value.foo);
27-
Assert.Equal("Foo", value.bar);
28-
}
21+
2922

3023
[Fact]
3124
public void DeserializeObjectToStruct() {
@@ -99,18 +92,6 @@ public void DeserializeNestedStruct() {
9992
Assert.Equal("two", value.Second.foo);
10093
}
10194

102-
[Fact]
103-
public void DeserializeStructReference() {
104-
var value = PhpSerialization.Deserialize<BStruct>(
105-
"""a:2:{s:5:"First";a:2:{s:3:"foo";s:3:"one";s:3:"bar";s:3:"two";}s:6:"Second";R:2;}"""
106-
);
107-
108-
Assert.Equal("one", value.First.foo);
109-
Assert.Equal("two", value.First.bar);
110-
Assert.Equal("one", value.Second.foo);
111-
Assert.Equal("two", value.Second.bar);
112-
}
113-
11495
[Fact]
11596
public void DeserializeNullToStruct() {
11697
Assert.Equal(

PhpSerializerNET.Test/Deserialize/ObjectDeserialization.cs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,6 @@ This Source Code Form is subject to the terms of the Mozilla Public
1212
namespace PhpSerializerNET.Test.Deserialize;
1313

1414
public class ObjectDeserializationTest {
15-
[Fact]
16-
public void References() {
17-
var result = PhpSerialization.Deserialize<MixedKeysPhpClass>(
18-
"""O:8:"stdClass":4:{i:0;s:3:"Foo";i:1;R:2;s:1:"a";s:1:"A";s:1:"b";R:3;}"""
19-
);
20-
Assert.NotNull(result);
21-
Assert.NotNull(result);
22-
Assert.Equal("Foo", result.Foo);
23-
Assert.Equal("Foo", result.Bar);
24-
Assert.Equal("A", result.Baz);
25-
Assert.Equal("A", result.Dummy);
26-
27-
result = PhpSerialization.Deserialize<MixedKeysPhpClass>(
28-
"""O:8:"stdClass":4:{i:0;s:3:"Foo";i:1;R:2;s:1:"a";s:1:"A";s:1:"b";R:2;}"""
29-
);
30-
Assert.NotNull(result);
31-
Assert.NotNull(result);
32-
Assert.Equal("Foo", result.Foo);
33-
Assert.Equal("Foo", result.Bar);
34-
Assert.Equal("A", result.Baz);
35-
Assert.Equal("Foo", result.Dummy);
36-
}
3715

3816
[Fact]
3917
public void IntegerKeysClass() {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
/**
3+
This Source Code Form is subject to the terms of the Mozilla Public
4+
License, v. 2.0. If a copy of the MPL was not distributed with this
5+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
**/
7+
8+
using Xunit;
9+
using PhpSerializerNET.Test.DataTypes;
10+
11+
namespace PhpSerializerNET.Test.Deserialize;
12+
13+
public class ReferenceDeserializationTest {
14+
[Fact]
15+
public void InObject() {
16+
var result = PhpSerialization.Deserialize<MixedKeysPhpClass>(
17+
"""O:8:"stdClass":4:{i:0;s:3:"Foo";i:1;R:2;s:1:"a";s:1:"A";s:1:"b";R:3;}"""
18+
);
19+
Assert.NotNull(result);
20+
Assert.NotNull(result);
21+
Assert.Equal("Foo", result.Foo);
22+
Assert.Equal("Foo", result.Bar);
23+
Assert.Equal("A", result.Baz);
24+
Assert.Equal("A", result.Dummy);
25+
26+
result = PhpSerialization.Deserialize<MixedKeysPhpClass>(
27+
"""O:8:"stdClass":4:{i:0;s:3:"Foo";i:1;R:2;s:1:"a";s:1:"A";s:1:"b";R:2;}"""
28+
);
29+
Assert.NotNull(result);
30+
Assert.NotNull(result);
31+
Assert.Equal("Foo", result.Foo);
32+
Assert.Equal("Foo", result.Bar);
33+
Assert.Equal("A", result.Baz);
34+
Assert.Equal("Foo", result.Dummy);
35+
}
36+
37+
[Fact]
38+
public void InArray() {
39+
var value = PhpSerialization.Deserialize<AStruct>(
40+
"a:2:{s:3:\"foo\";s:3:\"Foo\";s:3:\"bar\";R:2;}"
41+
);
42+
Assert.Equal("Foo", value.foo);
43+
Assert.Equal("Foo", value.bar);
44+
}
45+
46+
[Fact]
47+
public void ReferencingArray() {
48+
var value = PhpSerialization.Deserialize<BStruct>(
49+
"""a:2:{s:5:"First";a:2:{s:3:"foo";s:3:"one";s:3:"bar";s:3:"two";}s:6:"Second";R:2;}"""
50+
);
51+
52+
Assert.Equal("one", value.First.foo);
53+
Assert.Equal("two", value.First.bar);
54+
Assert.Equal("one", value.Second.foo);
55+
Assert.Equal("two", value.Second.bar);
56+
}
57+
58+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
This Source Code Form is subject to the terms of the Mozilla Public
3+
License, v. 2.0. If a copy of the MPL was not distributed with this
4+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
5+
**/
6+
7+
using Xunit;
8+
9+
namespace PhpSerializerNET.Test.Deserialize.Validation;
10+
11+
public class TestReferenceValidation {
12+
[Theory]
13+
[InlineData("r", "Unexpected end of input. Expected ':' at index 1, but input ends at index 0")]
14+
[InlineData("r:1", "Unexpected token at index 2: Expected number, but found ';' instead.")]
15+
[InlineData("r:1;", "Invalid reference: '1' can not be resolved.")]
16+
public void ThrowsOnInvalidReference(string input, string exceptionMessage) {
17+
var ex = Assert.Throws<DeserializationException>(() => PhpSerialization.Deserialize(input));
18+
Assert.Equal(exceptionMessage, ex.Message);
19+
}
20+
}

PhpSerializerNET/Deserialization/PhpToken.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace PhpSerializerNET;
1212
/// <summary>
1313
/// PHP data token. Holds the type, position (in the input string), length and value.
1414
/// </summary>
15-
[StructLayout(LayoutKind.Sequential, Size = 1)]
15+
[StructLayout(LayoutKind.Sequential, Pack = 1)]
1616
internal readonly struct PhpToken {
1717
internal readonly PhpDataType Type;
1818
internal readonly int Position;
@@ -30,9 +30,9 @@ internal readonly struct PhpToken {
3030
internal readonly ValueSpan Value;
3131

3232
internal PhpToken(
33-
in PhpDataType type,
34-
in int position,
35-
in ValueSpan value,
33+
PhpDataType type,
34+
int position,
35+
ValueSpan value,
3636
int reference,
3737
int length = 0,
3838
int lastValuePosition = 0

PhpSerializerNET/Deserialization/PhpTokenValidator.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ This Source Code Form is subject to the terms of the Mozilla Public
55
**/
66

77
using System;
8-
using System.Runtime.CompilerServices;
98

109
namespace PhpSerializerNET;
1110

@@ -90,7 +89,6 @@ private void GetCharacter(char character) {
9089
}
9190
}
9291

93-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
9492
private void GetFloat() {
9593
int i = this._position;
9694
for (; this._input[i] != (byte)';' && i < this._lastIndex; i++) {
@@ -123,7 +121,6 @@ private void GetFloat() {
123121
}
124122
}
125123

126-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
127124
private void GetInteger() {
128125
int i = this._position;
129126
for (; this._input[i] != ';' && i < this._lastIndex; i++) {
@@ -152,7 +149,6 @@ private void GetInteger() {
152149
}
153150
}
154151

155-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
156152
private void GetLength(PhpDataType dataType, ref int length) {
157153
for (; this._input[this._position] != ':' && this._position < this._lastIndex; this._position++) {
158154
length = this._input[this._position] switch {
@@ -164,7 +160,6 @@ private void GetLength(PhpDataType dataType, ref int length) {
164160
}
165161
}
166162

167-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
168163
private void GetBoolean() {
169164
if (this._lastIndex < this._position) {
170165
throw new DeserializationException(
@@ -180,7 +175,6 @@ private void GetBoolean() {
180175
}
181176
}
182177

183-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
184178
private void GetNCharacters(int length) {
185179
if (this._position + length > this._lastIndex) {
186180
throw new DeserializationException(
@@ -190,7 +184,6 @@ private void GetNCharacters(int length) {
190184
this._position += length;
191185
}
192186

193-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
194187
private void GetObjectToken() {
195188
int position = this._position - 1;
196189
int classNamelength = 0;
@@ -221,7 +214,7 @@ private void GetObjectToken() {
221214
this.GetCharacter('}');
222215
}
223216

224-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
217+
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
225218
private void GetArrayToken() {
226219
int position = this._position - 1;
227220
this.GetCharacter(':');
@@ -251,7 +244,7 @@ private void GetArrayToken() {
251244
/// <param name="input"> The raw UTF8 bytes of PHP data to validate. </param>
252245
/// <returns> The number of tokens found. </returns>
253246
/// <exception cref="DeserializationException"></exception>
254-
internal static int Validate(ReadOnlySpan<byte> input) {
247+
internal static int Validate(in ReadOnlySpan<byte> input) {
255248
var validatior = new PhpTokenValidator(input);
256249
validatior.GetToken();
257250
if (validatior._position <= validatior._lastIndex) {

PhpSerializerNET/Deserialization/PhpTokenizer.cs

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ This Source Code Form is subject to the terms of the Mozilla Public
55
**/
66

77
using System;
8-
using System.Collections.Generic;
9-
using System.Runtime.CompilerServices;
108

119
#nullable enable
1210

@@ -19,7 +17,7 @@ public ref struct PhpTokenizer {
1917
private int _tokenPosition;
2018
private int _reference = 0;
2119

22-
private PhpTokenizer(ReadOnlySpan<byte> input, Span<PhpToken> array) {
20+
private PhpTokenizer(in ReadOnlySpan<byte> input, Span<PhpToken> array) {
2321
this._input = input;
2422
this._tokens = array;
2523
this._position = 0;
@@ -30,31 +28,28 @@ private void Advance(int positions) {
3028
this._position += positions;
3129
}
3230

33-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3431
private ValueSpan GetNumbers() {
3532
int start = this._position;
3633
while (this._input[++this._position] != (byte)';') { }
3734
return new ValueSpan(start, this._position - start);
3835
}
3936

40-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4137
private int GetLength() {
4238
int result = 0;
43-
for (; this._input[this._position] != ':'; this._position++) {
39+
for (; this._input[this._position] != (byte)':'; this._position++) {
4440
result = result * 10 + (this._input[this._position] - 48);
4541
}
4642
return result;
4743
}
4844

49-
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
50-
private void GetToken(in bool countReference) {
45+
private void GetToken(bool countReference) {
5146
switch (this._input[this._position++]) {
5247
case (byte)'r':
5348
case (byte)'R':
5449
this.GetReferenceToken();
5550
break;
5651
case (byte)'b':
57-
this.GetBooleanToken(in countReference);
52+
this.GetBooleanToken(countReference);
5853
break;
5954
case (byte)'N':
6055
this._tokens[this._tokenPosition++] = new PhpToken(
@@ -66,25 +61,24 @@ private void GetToken(in bool countReference) {
6661
this._position++;
6762
break;
6863
case (byte)'s':
69-
this.GetStringToken(in countReference);
64+
this.GetStringToken(countReference);
7065
break;
7166
case (byte)'i':
72-
this.GetIntegerToken(in countReference);
67+
this.GetIntegerToken(countReference);
7368
break;
7469
case (byte)'d':
75-
this.GetFloatingToken(in countReference);
70+
this.GetFloatingToken(countReference);
7671
break;
7772
case (byte)'a':
78-
this.GetArrayToken(in countReference);
73+
this.GetArrayToken(countReference);
7974
break;
8075
case (byte)'O':
81-
this.GetObjectToken(in countReference);
76+
this.GetObjectToken(countReference);
8277
break;
8378
}
8479
}
8580

86-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
87-
private void GetBooleanToken(ref readonly bool reference) {
81+
private void GetBooleanToken(bool reference) {
8882
this._position++;
8983
this._tokens[this._tokenPosition++] = new PhpToken(
9084
PhpDataType.Boolean,
@@ -95,8 +89,7 @@ private void GetBooleanToken(ref readonly bool reference) {
9589
this._position++;
9690
}
9791

98-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
99-
private void GetStringToken(ref readonly bool reference) {
92+
private void GetStringToken(bool reference) {
10093
int position = this._position - 1;
10194
this._position++;
10295
int length = this.GetLength();
@@ -110,8 +103,7 @@ private void GetStringToken(ref readonly bool reference) {
110103
this._position += 2 + length;
111104
}
112105

113-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
114-
private void GetIntegerToken(ref readonly bool reference) {
106+
private void GetIntegerToken(bool reference) {
115107
this._position++;
116108
this._tokens[this._tokenPosition++] = new PhpToken(
117109
PhpDataType.Integer,
@@ -122,7 +114,6 @@ private void GetIntegerToken(ref readonly bool reference) {
122114
this._position++;
123115
}
124116

125-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
126117
private void GetReferenceToken() {
127118
this._position++;
128119
int index = this.GetNumbers().GetInt(this._input);
@@ -133,8 +124,7 @@ private void GetReferenceToken() {
133124
this._position++;
134125
}
135126

136-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
137-
private void GetFloatingToken(ref readonly bool reference) {
127+
private void GetFloatingToken(bool reference) {
138128
this._position++;
139129
this._tokens[this._tokenPosition++] = new PhpToken(
140130
PhpDataType.Floating,
@@ -145,8 +135,7 @@ private void GetFloatingToken(ref readonly bool reference) {
145135
this._position++;
146136
}
147137

148-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
149-
private void GetArrayToken(ref readonly bool reference) {
138+
private void GetArrayToken(bool reference) {
150139
var tokenPosition = this._tokenPosition++;
151140
int position = this._position - 1;
152141
int referenceIndex = reference ? ++this._reference : 0;
@@ -168,8 +157,7 @@ private void GetArrayToken(ref readonly bool reference) {
168157
this._position++;
169158
}
170159

171-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
172-
private void GetObjectToken(ref readonly bool reference) {
160+
private void GetObjectToken(bool reference) {
173161
var tokenPosition = this._tokenPosition++;
174162
int referenceIndex = reference ? ++this._reference : 0;
175163
int position = this._position - 1;
@@ -195,7 +183,7 @@ private void GetObjectToken(ref readonly bool reference) {
195183
this._position++;
196184
}
197185

198-
internal static void Tokenize(ReadOnlySpan<byte> inputBytes, Span<PhpToken> tokens) {
186+
internal static void Tokenize(ReadOnlySpan<byte> inputBytes, in Span<PhpToken> tokens) {
199187
new PhpTokenizer(inputBytes, tokens).GetToken(true);
200188
}
201189
}

0 commit comments

Comments
 (0)