parent
dfab275eca
commit
25755efc4f
152 changed files with 28848 additions and 1136 deletions
@ -0,0 +1,198 @@ |
||||
#region Copyright notice and license |
||||
// Protocol Buffers - Google's data interchange format |
||||
// Copyright 2019 Google Inc. All rights reserved. |
||||
// https://github.com/protocolbuffers/protobuf |
||||
// |
||||
// Redistribution and use in source and binary forms, with or without |
||||
// modification, are permitted provided that the following conditions are |
||||
// met: |
||||
// |
||||
// * Redistributions of source code must retain the above copyright |
||||
// notice, this list of conditions and the following disclaimer. |
||||
// * Redistributions in binary form must reproduce the above |
||||
// copyright notice, this list of conditions and the following disclaimer |
||||
// in the documentation and/or other materials provided with the |
||||
// distribution. |
||||
// * Neither the name of Google Inc. nor the names of its |
||||
// contributors may be used to endorse or promote products derived from |
||||
// this software without specific prior written permission. |
||||
// |
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
#endregion |
||||
|
||||
using BenchmarkDotNet.Attributes; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Buffers; |
||||
using Google.Protobuf.WellKnownTypes; |
||||
|
||||
namespace Google.Protobuf.Benchmarks |
||||
{ |
||||
/// <summary> |
||||
/// Benchmark that tests writing performance for various messages. |
||||
/// </summary> |
||||
[MemoryDiagnoser] |
||||
public class WriteMessagesBenchmark |
||||
{ |
||||
const int MaxMessages = 100; |
||||
|
||||
SubTest manyWrapperFieldsTest = new SubTest(ParseMessagesBenchmark.CreateManyWrapperFieldsMessage(), MaxMessages); |
||||
SubTest manyPrimitiveFieldsTest = new SubTest(ParseMessagesBenchmark.CreateManyPrimitiveFieldsMessage(), MaxMessages); |
||||
SubTest emptyMessageTest = new SubTest(new Empty(), MaxMessages); |
||||
|
||||
public IEnumerable<int> MessageCountValues => new[] { 10, 100 }; |
||||
|
||||
[GlobalSetup] |
||||
public void GlobalSetup() |
||||
{ |
||||
} |
||||
|
||||
[Benchmark] |
||||
public byte[] ManyWrapperFieldsMessage_ToByteArray() |
||||
{ |
||||
return manyWrapperFieldsTest.ToByteArray(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public byte[] ManyWrapperFieldsMessage_WriteToCodedOutputStream() |
||||
{ |
||||
return manyWrapperFieldsTest.WriteToCodedOutputStream_PreAllocatedBuffer(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public byte[] ManyWrapperFieldsMessage_WriteToSpan() |
||||
{ |
||||
return manyWrapperFieldsTest.WriteToSpan_PreAllocatedBuffer(); |
||||
} |
||||
|
||||
|
||||
[Benchmark] |
||||
public byte[] ManyPrimitiveFieldsMessage_ToByteArray() |
||||
{ |
||||
return manyPrimitiveFieldsTest.ToByteArray(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public byte[] ManyPrimitiveFieldsMessage_WriteToCodedOutputStream() |
||||
{ |
||||
return manyPrimitiveFieldsTest.WriteToCodedOutputStream_PreAllocatedBuffer(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public byte[] ManyPrimitiveFieldsMessage_WriteToSpan() |
||||
{ |
||||
return manyPrimitiveFieldsTest.WriteToSpan_PreAllocatedBuffer(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public byte[] EmptyMessage_ToByteArray() |
||||
{ |
||||
return emptyMessageTest.ToByteArray(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public byte[] EmptyMessage_WriteToCodedOutputStream() |
||||
{ |
||||
return emptyMessageTest.WriteToCodedOutputStream_PreAllocatedBuffer(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public byte[] EmptyMessage_WriteToSpan() |
||||
{ |
||||
return emptyMessageTest.WriteToSpan_PreAllocatedBuffer(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(MessageCountValues))] |
||||
public void ManyWrapperFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount) |
||||
{ |
||||
manyWrapperFieldsTest.WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(messageCount); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(MessageCountValues))] |
||||
public void ManyWrapperFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount) |
||||
{ |
||||
manyWrapperFieldsTest.WriteDelimitedMessagesToSpan_PreAllocatedBuffer(messageCount); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(MessageCountValues))] |
||||
public void ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToCodedOutputStream(int messageCount) |
||||
{ |
||||
manyPrimitiveFieldsTest.WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(messageCount); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(MessageCountValues))] |
||||
public void ManyPrimitiveFieldsMessage_WriteDelimitedMessagesToSpan(int messageCount) |
||||
{ |
||||
manyPrimitiveFieldsTest.WriteDelimitedMessagesToSpan_PreAllocatedBuffer(messageCount); |
||||
} |
||||
|
||||
private class SubTest |
||||
{ |
||||
private readonly IMessage message; |
||||
private readonly byte[] outputBuffer; |
||||
private readonly byte[] multipleMessagesOutputBuffer; |
||||
|
||||
public SubTest(IMessage message, int maxMessageCount) |
||||
{ |
||||
this.message = message; |
||||
|
||||
int messageSize = message.CalculateSize(); |
||||
this.outputBuffer = new byte[messageSize]; |
||||
this.multipleMessagesOutputBuffer = new byte[maxMessageCount * (messageSize + CodedOutputStream.ComputeLengthSize(messageSize))]; |
||||
} |
||||
|
||||
public byte[] ToByteArray() => message.ToByteArray(); |
||||
|
||||
public byte[] WriteToCodedOutputStream_PreAllocatedBuffer() |
||||
{ |
||||
var cos = new CodedOutputStream(outputBuffer); // use pre-existing output buffer |
||||
message.WriteTo(cos); |
||||
return outputBuffer; |
||||
} |
||||
|
||||
public byte[] WriteToSpan_PreAllocatedBuffer() |
||||
{ |
||||
var span = new Span<byte>(outputBuffer); // use pre-existing output buffer |
||||
message.WriteTo(span); |
||||
return outputBuffer; |
||||
} |
||||
|
||||
public byte[] WriteDelimitedMessagesToCodedOutputStream_PreAllocatedBuffer(int messageCount) |
||||
{ |
||||
var cos = new CodedOutputStream(multipleMessagesOutputBuffer); // use pre-existing output buffer |
||||
for (int i = 0; i < messageCount; i++) |
||||
{ |
||||
cos.WriteMessage(message); |
||||
} |
||||
return multipleMessagesOutputBuffer; |
||||
} |
||||
|
||||
public byte[] WriteDelimitedMessagesToSpan_PreAllocatedBuffer(int messageCount) |
||||
{ |
||||
var span = new Span<byte>(multipleMessagesOutputBuffer); // use pre-existing output buffer |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
for (int i = 0; i < messageCount; i++) |
||||
{ |
||||
ctx.WriteMessage(message); |
||||
} |
||||
return multipleMessagesOutputBuffer; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,519 @@ |
||||
#region Copyright notice and license |
||||
// Protocol Buffers - Google's data interchange format |
||||
// Copyright 2019 Google Inc. All rights reserved. |
||||
// https://github.com/protocolbuffers/protobuf |
||||
// |
||||
// Redistribution and use in source and binary forms, with or without |
||||
// modification, are permitted provided that the following conditions are |
||||
// met: |
||||
// |
||||
// * Redistributions of source code must retain the above copyright |
||||
// notice, this list of conditions and the following disclaimer. |
||||
// * Redistributions in binary form must reproduce the above |
||||
// copyright notice, this list of conditions and the following disclaimer |
||||
// in the documentation and/or other materials provided with the |
||||
// distribution. |
||||
// * Neither the name of Google Inc. nor the names of its |
||||
// contributors may be used to endorse or promote products derived from |
||||
// this software without specific prior written permission. |
||||
// |
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
#endregion |
||||
|
||||
using BenchmarkDotNet.Attributes; |
||||
using System; |
||||
using System.Buffers.Binary; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Buffers; |
||||
using System.Text; |
||||
|
||||
namespace Google.Protobuf.Benchmarks |
||||
{ |
||||
/// <summary> |
||||
/// Benchmarks throughput when writing raw primitives. |
||||
/// </summary> |
||||
[MemoryDiagnoser] |
||||
public class WriteRawPrimitivesBenchmark |
||||
{ |
||||
// key is the encodedSize of varint values |
||||
Dictionary<int, uint[]> varint32Values; |
||||
Dictionary<int, ulong[]> varint64Values; |
||||
|
||||
double[] doubleValues; |
||||
float[] floatValues; |
||||
|
||||
// key is the encodedSize of string values |
||||
Dictionary<int, string[]> stringValues; |
||||
|
||||
// key is the encodedSize of string values |
||||
Dictionary<int, string[]> nonAsciiStringValues; |
||||
|
||||
// key is the encodedSize of string values |
||||
Dictionary<int, ByteString[]> byteStringValues; |
||||
|
||||
// the buffer to which all the data will be written |
||||
byte[] outputBuffer; |
||||
|
||||
Random random = new Random(417384220); // random but deterministic seed |
||||
|
||||
public IEnumerable<int> StringEncodedSizes => new[] { 1, 4, 10, 105, 10080 }; |
||||
|
||||
public IEnumerable<int> NonAsciiStringEncodedSizes => new[] { 4, 10, 105, 10080 }; |
||||
|
||||
[GlobalSetup] |
||||
public void GlobalSetup() |
||||
{ |
||||
outputBuffer = new byte[BytesToWrite]; |
||||
|
||||
varint32Values = new Dictionary<int, uint[]>(); |
||||
varint64Values = new Dictionary<int, ulong[]>(); |
||||
for (int encodedSize = 1; encodedSize <= 10; encodedSize++) |
||||
{ |
||||
if (encodedSize <= 5) |
||||
{ |
||||
varint32Values.Add(encodedSize, CreateRandomVarints32(random, BytesToWrite / encodedSize, encodedSize)); |
||||
} |
||||
varint64Values.Add(encodedSize, CreateRandomVarints64(random, BytesToWrite / encodedSize, encodedSize)); |
||||
} |
||||
|
||||
doubleValues = CreateRandomDoubles(random, BytesToWrite / sizeof(double)); |
||||
floatValues = CreateRandomFloats(random, BytesToWrite / sizeof(float)); |
||||
|
||||
stringValues = new Dictionary<int, string[]>(); |
||||
|
||||
byteStringValues = new Dictionary<int, ByteString[]>(); |
||||
foreach(var encodedSize in StringEncodedSizes) |
||||
{ |
||||
stringValues.Add(encodedSize, CreateStrings(BytesToWrite / encodedSize, encodedSize)); |
||||
byteStringValues.Add(encodedSize, CreateByteStrings(BytesToWrite / encodedSize, encodedSize)); |
||||
} |
||||
|
||||
nonAsciiStringValues = new Dictionary<int, string[]>(); |
||||
foreach(var encodedSize in NonAsciiStringEncodedSizes) |
||||
{ |
||||
nonAsciiStringValues.Add(encodedSize, CreateNonAsciiStrings(BytesToWrite / encodedSize, encodedSize)); |
||||
} |
||||
} |
||||
|
||||
// Total number of bytes that each benchmark will write. |
||||
// Measuring the time taken to write buffer of given size makes it easier to compare parsing speed for different |
||||
// types and makes it easy to calculate the througput (in MB/s) |
||||
// 10800 bytes is chosen because it is divisible by all possible encoded sizes for all primitive types {1..10} |
||||
[Params(10080)] |
||||
public int BytesToWrite { get; set; } |
||||
|
||||
[Benchmark] |
||||
[Arguments(1)] |
||||
[Arguments(2)] |
||||
[Arguments(3)] |
||||
[Arguments(4)] |
||||
[Arguments(5)] |
||||
public void WriteRawVarint32_CodedOutputStream(int encodedSize) |
||||
{ |
||||
var values = varint32Values[encodedSize]; |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
for (int i = 0; i < values.Length; i++) |
||||
{ |
||||
cos.WriteRawVarint32(values[i]); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[Arguments(1)] |
||||
[Arguments(2)] |
||||
[Arguments(3)] |
||||
[Arguments(4)] |
||||
[Arguments(5)] |
||||
public void WriteRawVarint32_WriteContext(int encodedSize) |
||||
{ |
||||
var values = varint32Values[encodedSize]; |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
for (int i = 0; i < values.Length; i++) |
||||
{ |
||||
ctx.WriteUInt32(values[i]); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[Arguments(1)] |
||||
[Arguments(2)] |
||||
[Arguments(3)] |
||||
[Arguments(4)] |
||||
[Arguments(5)] |
||||
[Arguments(6)] |
||||
[Arguments(7)] |
||||
[Arguments(8)] |
||||
[Arguments(9)] |
||||
[Arguments(10)] |
||||
public void WriteRawVarint64_CodedOutputStream(int encodedSize) |
||||
{ |
||||
var values = varint64Values[encodedSize]; |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
for (int i = 0; i < values.Length; i++) |
||||
{ |
||||
cos.WriteRawVarint64(values[i]); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[Arguments(1)] |
||||
[Arguments(2)] |
||||
[Arguments(3)] |
||||
[Arguments(4)] |
||||
[Arguments(5)] |
||||
[Arguments(6)] |
||||
[Arguments(7)] |
||||
[Arguments(8)] |
||||
[Arguments(9)] |
||||
[Arguments(10)] |
||||
public void WriteRawVarint64_WriteContext(int encodedSize) |
||||
{ |
||||
var values = varint64Values[encodedSize]; |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
for (int i = 0; i < values.Length; i++) |
||||
{ |
||||
ctx.WriteUInt64(values[i]); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteFixed32_CodedOutputStream() |
||||
{ |
||||
const int encodedSize = sizeof(uint); |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
for (int i = 0; i < BytesToWrite / encodedSize; i++) |
||||
{ |
||||
cos.WriteFixed32(12345); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteFixed32_WriteContext() |
||||
{ |
||||
const int encodedSize = sizeof(uint); |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
for (uint i = 0; i < BytesToWrite / encodedSize; i++) |
||||
{ |
||||
ctx.WriteFixed32(12345); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteFixed64_CodedOutputStream() |
||||
{ |
||||
const int encodedSize = sizeof(ulong); |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
for(int i = 0; i < BytesToWrite / encodedSize; i++) |
||||
{ |
||||
cos.WriteFixed64(123456789); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteFixed64_WriteContext() |
||||
{ |
||||
const int encodedSize = sizeof(ulong); |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
for (uint i = 0; i < BytesToWrite / encodedSize; i++) |
||||
{ |
||||
ctx.WriteFixed64(123456789); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteRawTag_OneByte_WriteContext() |
||||
{ |
||||
const int encodedSize = 1; |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
for (uint i = 0; i < BytesToWrite / encodedSize; i++) |
||||
{ |
||||
ctx.WriteRawTag(16); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteRawTag_TwoBytes_WriteContext() |
||||
{ |
||||
const int encodedSize = 2; |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
for (uint i = 0; i < BytesToWrite / encodedSize; i++) |
||||
{ |
||||
ctx.WriteRawTag(137, 6); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteRawTag_ThreeBytes_WriteContext() |
||||
{ |
||||
const int encodedSize = 3; |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
for (uint i = 0; i < BytesToWrite / encodedSize; i++) |
||||
{ |
||||
ctx.WriteRawTag(160, 131, 1); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void Baseline_WriteContext() |
||||
{ |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
ctx.state.position = outputBuffer.Length; |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteRawFloat_CodedOutputStream() |
||||
{ |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
foreach (var value in floatValues) |
||||
{ |
||||
cos.WriteFloat(value); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteRawFloat_WriteContext() |
||||
{ |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
foreach (var value in floatValues) |
||||
{ |
||||
ctx.WriteFloat(value); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteRawDouble_CodedOutputStream() |
||||
{ |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
foreach (var value in doubleValues) |
||||
{ |
||||
cos.WriteDouble(value); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
public void WriteRawDouble_WriteContext() |
||||
{ |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
foreach (var value in doubleValues) |
||||
{ |
||||
ctx.WriteDouble(value); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(StringEncodedSizes))] |
||||
public void WriteString_CodedOutputStream(int encodedSize) |
||||
{ |
||||
var values = stringValues[encodedSize]; |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
foreach (var value in values) |
||||
{ |
||||
cos.WriteString(value); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(StringEncodedSizes))] |
||||
public void WriteString_WriteContext(int encodedSize) |
||||
{ |
||||
var values = stringValues[encodedSize]; |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
foreach (var value in values) |
||||
{ |
||||
ctx.WriteString(value); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(NonAsciiStringEncodedSizes))] |
||||
public void WriteNonAsciiString_CodedOutputStream(int encodedSize) |
||||
{ |
||||
var values = nonAsciiStringValues[encodedSize]; |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
foreach (var value in values) |
||||
{ |
||||
cos.WriteString(value); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(NonAsciiStringEncodedSizes))] |
||||
public void WriteNonAsciiString_WriteContext(int encodedSize) |
||||
{ |
||||
var values = nonAsciiStringValues[encodedSize]; |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
foreach (var value in values) |
||||
{ |
||||
ctx.WriteString(value); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(StringEncodedSizes))] |
||||
public void WriteBytes_CodedOutputStream(int encodedSize) |
||||
{ |
||||
var values = byteStringValues[encodedSize]; |
||||
var cos = new CodedOutputStream(outputBuffer); |
||||
foreach (var value in values) |
||||
{ |
||||
cos.WriteBytes(value); |
||||
} |
||||
cos.Flush(); |
||||
cos.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
[Benchmark] |
||||
[ArgumentsSource(nameof(StringEncodedSizes))] |
||||
public void WriteBytes_WriteContext(int encodedSize) |
||||
{ |
||||
var values = byteStringValues[encodedSize]; |
||||
var span = new Span<byte>(outputBuffer); |
||||
WriteContext.Initialize(ref span, out WriteContext ctx); |
||||
foreach (var value in values) |
||||
{ |
||||
ctx.WriteBytes(value); |
||||
} |
||||
ctx.Flush(); |
||||
ctx.CheckNoSpaceLeft(); |
||||
} |
||||
|
||||
private static uint[] CreateRandomVarints32(Random random, int valueCount, int encodedSize) |
||||
{ |
||||
var result = new uint[valueCount]; |
||||
for (int i = 0; i < valueCount; i++) |
||||
{ |
||||
result[i] = (uint) ParseRawPrimitivesBenchmark.RandomUnsignedVarint(random, encodedSize, true); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private static ulong[] CreateRandomVarints64(Random random, int valueCount, int encodedSize) |
||||
{ |
||||
var result = new ulong[valueCount]; |
||||
for (int i = 0; i < valueCount; i++) |
||||
{ |
||||
result[i] = ParseRawPrimitivesBenchmark.RandomUnsignedVarint(random, encodedSize, false); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private static float[] CreateRandomFloats(Random random, int valueCount) |
||||
{ |
||||
var result = new float[valueCount]; |
||||
for (int i = 0; i < valueCount; i++) |
||||
{ |
||||
result[i] = (float)random.NextDouble(); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private static double[] CreateRandomDoubles(Random random, int valueCount) |
||||
{ |
||||
var result = new double[valueCount]; |
||||
for (int i = 0; i < valueCount; i++) |
||||
{ |
||||
result[i] = random.NextDouble(); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private static string[] CreateStrings(int valueCount, int encodedSize) |
||||
{ |
||||
var str = ParseRawPrimitivesBenchmark.CreateStringWithEncodedSize(encodedSize); |
||||
|
||||
var result = new string[valueCount]; |
||||
for (int i = 0; i < valueCount; i++) |
||||
{ |
||||
result[i] = str; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private static string[] CreateNonAsciiStrings(int valueCount, int encodedSize) |
||||
{ |
||||
var str = ParseRawPrimitivesBenchmark.CreateNonAsciiStringWithEncodedSize(encodedSize); |
||||
|
||||
var result = new string[valueCount]; |
||||
for (int i = 0; i < valueCount; i++) |
||||
{ |
||||
result[i] = str; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
private static ByteString[] CreateByteStrings(int valueCount, int encodedSize) |
||||
{ |
||||
var str = ParseRawPrimitivesBenchmark.CreateStringWithEncodedSize(encodedSize); |
||||
|
||||
var result = new ByteString[valueCount]; |
||||
for (int i = 0; i < valueCount; i++) |
||||
{ |
||||
result[i] = ByteString.CopyFrom(Encoding.UTF8.GetBytes(str)); |
||||
} |
||||
return result; |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,95 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Zend/zend_API.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Arena
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct Arena { |
||||
zend_object std; |
||||
upb_arena* arena; |
||||
} Arena; |
||||
|
||||
zend_class_entry *Arena_class_entry; |
||||
static zend_object_handlers Arena_object_handlers; |
||||
|
||||
// PHP Object Handlers /////////////////////////////////////////////////////////
|
||||
|
||||
static zend_object* Arena_Create(zend_class_entry *class_type) { |
||||
Arena *intern = emalloc(sizeof(Arena)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &Arena_object_handlers; |
||||
intern->arena = upb_arena_new(); |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
static void Arena_Free(zend_object* obj) { |
||||
Arena* intern = (Arena*)obj; |
||||
upb_arena_free(intern->arena); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
// C Functions from arena.h ////////////////////////////////////////////////////
|
||||
|
||||
void Arena_Init(zval* val) { |
||||
ZVAL_OBJ(val, Arena_Create(Arena_class_entry)); |
||||
} |
||||
|
||||
upb_arena *Arena_Get(zval *val) { |
||||
Arena *a = (Arena*)Z_OBJ_P(val); |
||||
return a->arena; |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module init.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// No public methods.
|
||||
static const zend_function_entry Arena_methods[] = { |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
void Arena_ModuleInit() { |
||||
zend_class_entry tmp_ce; |
||||
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Arena", Arena_methods); |
||||
Arena_class_entry = zend_register_internal_class(&tmp_ce); |
||||
Arena_class_entry->create_object = Arena_Create; |
||||
Arena_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
|
||||
memcpy(&Arena_object_handlers, &std_object_handlers, |
||||
sizeof(zend_object_handlers)); |
||||
Arena_object_handlers.free_obj = Arena_Free; |
||||
} |
@ -0,0 +1,47 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_ARENA_H_ |
||||
#define PHP_PROTOBUF_ARENA_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// Registers the PHP Arena class.
|
||||
void Arena_ModuleInit(); |
||||
|
||||
// Creates and returns a new arena object that wraps a new upb_arena*.
|
||||
void Arena_Init(zval *val); |
||||
|
||||
// Gets the underlying upb_arena from this arena object.
|
||||
upb_arena *Arena_Get(zval *arena); |
||||
|
||||
#endif // PHP_PROTOBUF_ARENA_H_
|
@ -0,0 +1,602 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "array.h" |
||||
|
||||
#include <Zend/zend_API.h> |
||||
#include <Zend/zend_interfaces.h> |
||||
|
||||
#include <ext/spl/spl_iterators.h> |
||||
|
||||
// This is not self-contained: it must be after other Zend includes.
|
||||
#include <Zend/zend_exceptions.h> |
||||
|
||||
#include "arena.h" |
||||
#include "convert.h" |
||||
#include "def.h" |
||||
#include "php-upb.h" |
||||
#include "protobuf.h" |
||||
|
||||
static void RepeatedFieldIter_make(zval *val, zval *repeated_field); |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RepeatedField
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval arena; |
||||
upb_array *array; |
||||
upb_fieldtype_t type; |
||||
const Descriptor* desc; // When values are messages.
|
||||
} RepeatedField; |
||||
|
||||
zend_class_entry *RepeatedField_class_entry; |
||||
static zend_object_handlers RepeatedField_object_handlers; |
||||
|
||||
// PHP Object Handlers /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* RepeatedField_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new RepeatedField |
||||
* object. |
||||
*/ |
||||
static zend_object* RepeatedField_create(zend_class_entry *class_type) { |
||||
RepeatedField *intern = emalloc(sizeof(RepeatedField)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &RepeatedField_object_handlers; |
||||
Arena_Init(&intern->arena); |
||||
intern->array = NULL; |
||||
intern->desc = NULL; |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField_dtor() |
||||
* |
||||
* Object handler to destroy a RepeatedField. This releases all resources |
||||
* associated with the message. Note that it is possible to access a destroyed |
||||
* object from PHP in rare cases. |
||||
*/ |
||||
static void RepeatedField_destructor(zend_object* obj) { |
||||
RepeatedField* intern = (RepeatedField*)obj; |
||||
ObjCache_Delete(intern->array); |
||||
zval_ptr_dtor(&intern->arena); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
static HashTable *RepeatedField_GetProperties(zval *object TSRMLS_DC) { |
||||
return NULL; // We do not have a properties table.
|
||||
} |
||||
|
||||
static zval *RepeatedField_GetPropertyPtrPtr(zval *object, zval *member, |
||||
int type, void **cache_slot) { |
||||
return NULL; // We don't offer direct references to our properties.
|
||||
} |
||||
|
||||
// C Functions from array.h ////////////////////////////////////////////////////
|
||||
|
||||
// These are documented in the header file.
|
||||
|
||||
void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr, |
||||
const upb_fielddef *f, zval *arena) { |
||||
if (!arr) { |
||||
ZVAL_NULL(val); |
||||
return; |
||||
} |
||||
|
||||
if (!ObjCache_Get(arr, val)) { |
||||
RepeatedField *intern = emalloc(sizeof(RepeatedField)); |
||||
zend_object_std_init(&intern->std, RepeatedField_class_entry); |
||||
intern->std.handlers = &RepeatedField_object_handlers; |
||||
ZVAL_COPY(&intern->arena, arena); |
||||
intern->array = arr; |
||||
intern->type = upb_fielddef_type(f); |
||||
intern->desc = Descriptor_GetFromFieldDef(f); |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
ObjCache_Add(intern->array, &intern->std); |
||||
ZVAL_OBJ(val, &intern->std); |
||||
} |
||||
} |
||||
|
||||
upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f, |
||||
upb_arena *arena) { |
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_ARRAY) { |
||||
// Auto-construct, eg. [1, 2, 3] -> upb_array([1, 2, 3]).
|
||||
upb_array *arr = upb_array_new(arena, upb_fielddef_type(f)); |
||||
HashTable *table = HASH_OF(val); |
||||
HashPosition pos; |
||||
upb_fieldtype_t type = upb_fielddef_type(f); |
||||
const Descriptor *desc = Descriptor_GetFromFieldDef(f); |
||||
|
||||
zend_hash_internal_pointer_reset_ex(table, &pos); |
||||
|
||||
while (true) { |
||||
zval *zv = zend_hash_get_current_data_ex(table, &pos); |
||||
upb_msgval val; |
||||
|
||||
if (!zv) return arr; |
||||
|
||||
if (!Convert_PhpToUpbAutoWrap(zv, &val, type, desc, arena)) { |
||||
return NULL; |
||||
} |
||||
|
||||
upb_array_append(arr, val, arena); |
||||
zend_hash_move_forward_ex(table, &pos); |
||||
} |
||||
} else if (Z_TYPE_P(val) == IS_OBJECT && |
||||
Z_OBJCE_P(val) == RepeatedField_class_entry) { |
||||
// Unwrap existing RepeatedField object to get the upb_array* inside.
|
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(val); |
||||
const Descriptor *desc = Descriptor_GetFromFieldDef(f); |
||||
|
||||
if (intern->type != upb_fielddef_type(f) || intern->desc != desc) { |
||||
php_error_docref(NULL, E_USER_ERROR, |
||||
"Wrong type for this repeated field."); |
||||
} |
||||
|
||||
upb_arena_fuse(arena, Arena_Get(&intern->arena)); |
||||
return intern->array; |
||||
} else { |
||||
php_error_docref(NULL, E_USER_ERROR, "Must be a repeated field"); |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
// RepeatedField PHP methods ///////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* RepeatedField::__construct() |
||||
* |
||||
* Constructs an instance of RepeatedField. |
||||
* @param long Type of the stored element. |
||||
* @param string Message/Enum class. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, __construct) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zend_long type; |
||||
zend_class_entry* klass = NULL; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|C", &type, &klass) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
intern->type = pbphp_dtype_to_type(type); |
||||
intern->desc = Descriptor_GetFromClassEntry(klass); |
||||
|
||||
if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) { |
||||
php_error_docref(NULL, E_USER_ERROR, |
||||
"Message/enum type must have concrete class."); |
||||
return; |
||||
} |
||||
|
||||
intern->array = upb_array_new(arena, intern->type); |
||||
ObjCache_Add(intern->array, &intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::append() |
||||
* |
||||
* Append element to the end of the repeated field. |
||||
* @param object The element to be added. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, append) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zval *php_val; |
||||
upb_msgval msgval; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &php_val) != SUCCESS || |
||||
!Convert_PhpToUpb(php_val, &msgval, intern->type, intern->desc, arena)) { |
||||
return; |
||||
} |
||||
|
||||
upb_array_append(intern->array, msgval, arena); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::offsetExists() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* isset($arr[$idx]); |
||||
* empty($arr[$idx]); |
||||
* |
||||
* @param long The index to be checked. |
||||
* @return bool True if the element at the given index exists. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, offsetExists) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
zend_long index; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
RETURN_BOOL(index >= 0 && index < upb_array_size(intern->array)); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::offsetGet() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* $x = $arr[$idx]; |
||||
* |
||||
* @param long The index of the element to be fetched. |
||||
* @return object The stored element at given index. |
||||
* @exception Invalid type for index. |
||||
* @exception Non-existing index. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, offsetGet) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
zend_long index; |
||||
upb_msgval msgval; |
||||
zval ret; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
if (index < 0 || index >= upb_array_size(intern->array)) { |
||||
zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index); |
||||
return; |
||||
} |
||||
|
||||
msgval = upb_array_get(intern->array, index); |
||||
Convert_UpbToPhp(msgval, &ret, intern->type, intern->desc, &intern->arena); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::offsetSet() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* $arr[$idx] = $x; |
||||
* $arr []= $x; // Append
|
||||
* |
||||
* @param long The index of the element to be assigned. |
||||
* @param object The element to be assigned. |
||||
* @exception Invalid type for index. |
||||
* @exception Non-existing index. |
||||
* @exception Incorrect type of the element. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, offsetSet) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
size_t size = upb_array_size(intern->array); |
||||
zval *offset, *val; |
||||
int64_t index; |
||||
upb_msgval msgval; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &offset, &val) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
if (Z_TYPE_P(offset) == IS_NULL) { |
||||
index = size; |
||||
} else if (!Convert_PhpToInt64(offset, &index)) { |
||||
return; |
||||
} |
||||
|
||||
if (!Convert_PhpToUpb(val, &msgval, intern->type, intern->desc, arena)) { |
||||
return; |
||||
} |
||||
|
||||
if (index > size) { |
||||
zend_error(E_USER_ERROR, "Element at index %ld doesn't exist.\n", index); |
||||
} else if (index == size) { |
||||
upb_array_append(intern->array, msgval, Arena_Get(&intern->arena)); |
||||
} else { |
||||
upb_array_set(intern->array, index, msgval); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::offsetUnset() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* unset($arr[$idx]); |
||||
* |
||||
* @param long The index of the element to be removed. |
||||
* @exception Invalid type for index. |
||||
* @exception The element to be removed is not at the end of the RepeatedField. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, offsetUnset) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
zend_long index; |
||||
zend_long size = upb_array_size(intern->array); |
||||
|
||||
// Only the element at the end of the array can be removed.
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
if (size == 0 || index != size - 1) { |
||||
php_error_docref(NULL, E_USER_ERROR, "Cannot remove element at %ld.\n", |
||||
index); |
||||
return; |
||||
} |
||||
|
||||
upb_array_resize(intern->array, size - 1, Arena_Get(&intern->arena)); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::count() |
||||
* |
||||
* Implements the Countable interface. Invoked when PHP code calls: |
||||
* |
||||
* $len = count($arr); |
||||
* Return the number of stored elements. |
||||
* This will also be called for: count($arr) |
||||
* @return long The number of stored elements. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, count) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
|
||||
if (zend_parse_parameters_none() == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
RETURN_LONG(upb_array_size(intern->array)); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::getIterator() |
||||
* |
||||
* Implements the IteratorAggregate interface. Invoked when PHP code calls: |
||||
* |
||||
* foreach ($arr) {} |
||||
* |
||||
* @return object Beginning iterator. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, getIterator) { |
||||
zval ret; |
||||
RepeatedFieldIter_make(&ret, getThis()); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1) |
||||
ZEND_ARG_INFO(0, index) |
||||
ZEND_END_ARG_INFO() |
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2) |
||||
ZEND_ARG_INFO(0, index) |
||||
ZEND_ARG_INFO(0, newval) |
||||
ZEND_END_ARG_INFO() |
||||
|
||||
ZEND_BEGIN_ARG_INFO(arginfo_void, 0) |
||||
ZEND_END_ARG_INFO() |
||||
|
||||
static zend_function_entry repeated_field_methods[] = { |
||||
PHP_ME(RepeatedField, __construct, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedField, append, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedField, offsetGet, arginfo_offsetGet, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedField, count, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedField, getIterator, arginfo_void, ZEND_ACC_PUBLIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PHP RepeatedFieldIter
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval repeated_field; |
||||
zend_long position; |
||||
} RepeatedFieldIter; |
||||
|
||||
zend_class_entry *RepeatedFieldIter_class_entry; |
||||
static zend_object_handlers repeated_field_iter_object_handlers; |
||||
|
||||
/**
|
||||
* RepeatedFieldIter_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new RepeatedFieldIter |
||||
* object. |
||||
*/ |
||||
zend_object* RepeatedFieldIter_create(zend_class_entry *class_type) { |
||||
RepeatedFieldIter *intern = emalloc(sizeof(RepeatedFieldIter)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &repeated_field_iter_object_handlers; |
||||
ZVAL_NULL(&intern->repeated_field); |
||||
intern->position = 0; |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter_dtor() |
||||
* |
||||
* Object handler to destroy a RepeatedFieldIter. This releases all resources |
||||
* associated with the message. Note that it is possible to access a destroyed |
||||
* object from PHP in rare cases. |
||||
*/ |
||||
static void RepeatedFieldIter_dtor(zend_object* obj) { |
||||
RepeatedFieldIter* intern = (RepeatedFieldIter*)obj; |
||||
zval_ptr_dtor(&intern->repeated_field); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter_make() |
||||
* |
||||
* C function to create a RepeatedFieldIter. |
||||
*/ |
||||
static void RepeatedFieldIter_make(zval *val, zval *repeated_field) { |
||||
RepeatedFieldIter *iter; |
||||
ZVAL_OBJ(val, RepeatedFieldIter_class_entry->create_object( |
||||
RepeatedFieldIter_class_entry)); |
||||
iter = (RepeatedFieldIter*)Z_OBJ_P(val); |
||||
ZVAL_COPY(&iter->repeated_field, repeated_field); |
||||
} |
||||
|
||||
/*
|
||||
* When a user writes: |
||||
* |
||||
* foreach($arr as $key => $val) {} |
||||
* |
||||
* PHP's iterator protocol is: |
||||
* |
||||
* $iter = $arr->getIterator(); |
||||
* for ($iter->rewind(); $iter->valid(); $iter->next()) { |
||||
* $key = $iter->key(); |
||||
* $val = $iter->current(); |
||||
* } |
||||
*/ |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::rewind() |
||||
* |
||||
* Implements the Iterator interface. Sets the iterator to the first element. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, rewind) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
intern->position = 0; |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::current() |
||||
* |
||||
* Implements the Iterator interface. Returns the current value. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, current) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
RepeatedField *field = (RepeatedField*)Z_OBJ_P(&intern->repeated_field); |
||||
upb_array *array = field->array; |
||||
zend_long index = intern->position; |
||||
upb_msgval msgval; |
||||
zval ret; |
||||
|
||||
if (index < 0 || index >= upb_array_size(array)) { |
||||
zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index); |
||||
} |
||||
|
||||
msgval = upb_array_get(array, index); |
||||
|
||||
Convert_UpbToPhp(msgval, &ret, field->type, field->desc, &field->arena); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::key() |
||||
* |
||||
* Implements the Iterator interface. Returns the current key. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, key) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
RETURN_LONG(intern->position); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::next() |
||||
* |
||||
* Implements the Iterator interface. Advances to the next element. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, next) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
++intern->position; |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::valid() |
||||
* |
||||
* Implements the Iterator interface. Returns true if this is a valid element. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, valid) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
RepeatedField *field = (RepeatedField*)Z_OBJ_P(&intern->repeated_field); |
||||
RETURN_BOOL(intern->position < upb_array_size(field->array)); |
||||
} |
||||
|
||||
static zend_function_entry repeated_field_iter_methods[] = { |
||||
PHP_ME(RepeatedFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module init.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Array_ModuleInit() |
||||
* |
||||
* Called when the C extension is loaded to register all types. |
||||
*/ |
||||
void Array_ModuleInit() { |
||||
zend_class_entry tmp_ce; |
||||
zend_object_handlers *h; |
||||
|
||||
// RepeatedField.
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\RepeatedField", |
||||
repeated_field_methods); |
||||
|
||||
RepeatedField_class_entry = zend_register_internal_class(&tmp_ce); |
||||
zend_class_implements(RepeatedField_class_entry, 3, spl_ce_ArrayAccess, |
||||
zend_ce_aggregate, spl_ce_Countable); |
||||
RepeatedField_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
RepeatedField_class_entry->create_object = RepeatedField_create; |
||||
|
||||
h = &RepeatedField_object_handlers; |
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = RepeatedField_destructor; |
||||
h->get_properties = RepeatedField_GetProperties; |
||||
h->get_property_ptr_ptr = RepeatedField_GetPropertyPtrPtr; |
||||
|
||||
// RepeatedFieldIter
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\RepeatedFieldIter", |
||||
repeated_field_iter_methods); |
||||
|
||||
RepeatedFieldIter_class_entry = zend_register_internal_class(&tmp_ce); |
||||
zend_class_implements(RepeatedFieldIter_class_entry, 1, zend_ce_iterator); |
||||
RepeatedFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
RepeatedFieldIter_class_entry->create_object = RepeatedFieldIter_create; |
||||
|
||||
h = &repeated_field_iter_object_handlers; |
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = RepeatedFieldIter_dtor; |
||||
} |
@ -0,0 +1,61 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_ARRAY_H_ |
||||
#define PHP_PROTOBUF_ARRAY_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// Registers PHP classes for RepeatedField.
|
||||
void Array_ModuleInit(); |
||||
|
||||
// Gets a upb_array* for the PHP object |val|:
|
||||
// * If |val| is a RepeatedField object, we first check its type and verify
|
||||
// that that the elements have the correct type for |f|. If so, we return the
|
||||
// wrapped upb_array*. We also make sure that this array's arena is fused to
|
||||
// |arena|, so the returned upb_array is guaranteed to live as long as
|
||||
// |arena|.
|
||||
// * If |val| is a PHP Array, we attempt to create a new upb_array using
|
||||
// |arena| and add all of the PHP elements to it.
|
||||
//
|
||||
// If an error occurs, we raise a PHP error and return NULL.
|
||||
upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f, upb_arena *arena); |
||||
|
||||
// Creates a PHP RepeatedField object for the given upb_array* and |f| and
|
||||
// returns it in |val|. The PHP object will keep a reference to this |arena| to
|
||||
// ensure the underlying array data stays alive.
|
||||
//
|
||||
// If |arr| is NULL, this will return a PHP null object.
|
||||
void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr, |
||||
const upb_fielddef *f, zval *arena); |
||||
|
||||
#endif // PHP_PROTOBUF_ARRAY_H_
|
@ -0,0 +1,46 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_BUNDLED_PHP_H_ |
||||
#define PHP_PROTOBUF_BUNDLED_PHP_H_ |
||||
|
||||
// We embed PHP source code into the binary for things we don't want to
|
||||
// implement in C. This struct serves as a table of contents for all of
|
||||
// the embedded files.
|
||||
typedef struct { |
||||
const char *filename; |
||||
const char *contents; |
||||
} BundledPhp_File; |
||||
|
||||
// An array of all the embedded file structs. This array is terminated with a
|
||||
// {NULL, NULL} entry.
|
||||
extern BundledPhp_File *bundled_files; |
||||
|
||||
#endif // PHP_PROTOBUF_BUNDLED_PHP_H_
|
@ -0,0 +1,10 @@ |
||||
PHP_ARG_ENABLE(protobuf, whether to enable Protobuf extension, [ --enable-protobuf Enable Protobuf extension]) |
||||
|
||||
if test "$PHP_PROTOBUF" != "no"; then |
||||
|
||||
PHP_NEW_EXTENSION( |
||||
protobuf, |
||||
arena.c array.c bundled_php.c convert.c def.c map.c message.c names.c php-upb.c protobuf.c, |
||||
$ext_shared) |
||||
|
||||
fi |
@ -0,0 +1,478 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "convert.h" |
||||
|
||||
#include <php.h> |
||||
|
||||
// This is not self-contained: it must be after other Zend includes.
|
||||
#include <Zend/zend_exceptions.h> |
||||
|
||||
#include "array.h" |
||||
#include "map.h" |
||||
#include "message.h" |
||||
#include "php-upb.h" |
||||
#include "protobuf.h" |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GPBUtil
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static zend_class_entry* GPBUtil_class_entry; |
||||
|
||||
// The implementation of type checking for primitive fields is empty. This is
|
||||
// because type checking is done when direct assigning message fields (e.g.,
|
||||
// foo->a = 1). Functions defined here are place holders in generated code for
|
||||
// pure PHP implementation (c extension and pure PHP share the same generated
|
||||
// code).
|
||||
|
||||
PHP_METHOD(Util, checkInt32) {} |
||||
PHP_METHOD(Util, checkUint32) {} |
||||
PHP_METHOD(Util, checkInt64) {} |
||||
PHP_METHOD(Util, checkUint64) {} |
||||
PHP_METHOD(Util, checkEnum) {} |
||||
PHP_METHOD(Util, checkFloat) {} |
||||
PHP_METHOD(Util, checkDouble) {} |
||||
PHP_METHOD(Util, checkBool) {} |
||||
PHP_METHOD(Util, checkString) {} |
||||
PHP_METHOD(Util, checkBytes) {} |
||||
PHP_METHOD(Util, checkMessage) {} |
||||
|
||||
// The result of checkMapField() is assigned, so we need to return the first
|
||||
// param:
|
||||
// $arr = GPBUtil::checkMapField($var,
|
||||
// \Google\Protobuf\Internal\GPBType::INT64,
|
||||
// \Google\Protobuf\Internal\GPBType::INT32);
|
||||
PHP_METHOD(Util, checkMapField) { |
||||
zval *val, *key_type, *val_type, *klass; |
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|z", &val, &key_type, |
||||
&val_type, &klass) == FAILURE) { |
||||
return; |
||||
} |
||||
RETURN_ZVAL(val, 1, 0); |
||||
} |
||||
|
||||
// The result of checkRepeatedField() is assigned, so we need to return the
|
||||
// first param:
|
||||
// $arr = GPBUtil::checkRepeatedField(
|
||||
// $var, \Google\Protobuf\Internal\GPBType::STRING);
|
||||
PHP_METHOD(Util, checkRepeatedField) { |
||||
zval *val, *type, *klass; |
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|z", &val, &type, &klass) == |
||||
FAILURE) { |
||||
return; |
||||
} |
||||
RETURN_ZVAL(val, 1, 0); |
||||
} |
||||
|
||||
static zend_function_entry util_methods[] = { |
||||
PHP_ME(Util, checkInt32, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkUint32, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkInt64, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkUint64, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkEnum, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkFloat, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkDouble, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkBool, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkBytes, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkMapField, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkRepeatedField, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Conversion functions used from C
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
upb_fieldtype_t pbphp_dtype_to_type(upb_descriptortype_t type) { |
||||
switch (type) { |
||||
#define CASE(descriptor_type, type) \ |
||||
case UPB_DESCRIPTOR_TYPE_##descriptor_type: \
|
||||
return UPB_TYPE_##type; |
||||
|
||||
CASE(FLOAT, FLOAT); |
||||
CASE(DOUBLE, DOUBLE); |
||||
CASE(BOOL, BOOL); |
||||
CASE(STRING, STRING); |
||||
CASE(BYTES, BYTES); |
||||
CASE(MESSAGE, MESSAGE); |
||||
CASE(GROUP, MESSAGE); |
||||
CASE(ENUM, ENUM); |
||||
CASE(INT32, INT32); |
||||
CASE(INT64, INT64); |
||||
CASE(UINT32, UINT32); |
||||
CASE(UINT64, UINT64); |
||||
CASE(SINT32, INT32); |
||||
CASE(SINT64, INT64); |
||||
CASE(FIXED32, UINT32); |
||||
CASE(FIXED64, UINT64); |
||||
CASE(SFIXED32, INT32); |
||||
CASE(SFIXED64, INT64); |
||||
|
||||
#undef CASE |
||||
|
||||
} |
||||
|
||||
zend_error(E_ERROR, "Unknown field type."); |
||||
return 0; |
||||
} |
||||
|
||||
static bool buftouint64(const char *ptr, const char *end, uint64_t *val) { |
||||
uint64_t u64 = 0; |
||||
while (ptr < end) { |
||||
unsigned ch = (unsigned)(*ptr - '0'); |
||||
if (ch >= 10) break; |
||||
if (u64 > UINT64_MAX / 10 || u64 * 10 > UINT64_MAX - ch) { |
||||
return false; |
||||
} |
||||
u64 *= 10; |
||||
u64 += ch; |
||||
ptr++; |
||||
} |
||||
|
||||
if (ptr != end) { |
||||
// In PHP tradition, we allow truncation: "1.1" -> 1.
|
||||
// But we don't allow 'e', eg. '1.1e2' or any other non-numeric chars.
|
||||
if (*ptr++ != '.') return false; |
||||
|
||||
for (;ptr < end; ptr++) { |
||||
if (*ptr < '0' || *ptr > '9') { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
*val = u64; |
||||
return true; |
||||
} |
||||
|
||||
static bool buftoint64(const char *ptr, const char *end, int64_t *val) { |
||||
bool neg = false; |
||||
uint64_t u64; |
||||
|
||||
if (ptr != end && *ptr == '-') { |
||||
ptr++; |
||||
neg = true; |
||||
} |
||||
|
||||
if (!buftouint64(ptr, end, &u64) || |
||||
u64 > (uint64_t)INT64_MAX + neg) { |
||||
return false; |
||||
} |
||||
|
||||
*val = neg ? -u64 : u64; |
||||
return true; |
||||
} |
||||
|
||||
static void throw_conversion_exception(const char *to, const zval *zv) { |
||||
zval tmp; |
||||
ZVAL_COPY(&tmp, zv); |
||||
convert_to_string(&tmp); |
||||
|
||||
zend_throw_exception_ex(NULL, 0, "Cannot convert '%s' to %s", |
||||
Z_STRVAL_P(&tmp), to); |
||||
|
||||
zval_ptr_dtor(&tmp); |
||||
} |
||||
|
||||
bool Convert_PhpToInt64(const zval *php_val, int64_t *i64) { |
||||
switch (Z_TYPE_P(php_val)) { |
||||
case IS_LONG: |
||||
*i64 = Z_LVAL_P(php_val); |
||||
return true; |
||||
case IS_DOUBLE: { |
||||
double dbl = Z_DVAL_P(php_val); |
||||
if (dbl > 9223372036854774784.0 || dbl < -9223372036854775808.0) { |
||||
zend_throw_exception_ex(NULL, 0, "Out of range"); |
||||
return false; |
||||
} |
||||
*i64 = dbl; /* must be guarded, overflow here is UB */ |
||||
return true; |
||||
} |
||||
case IS_STRING: { |
||||
const char *buf = Z_STRVAL_P(php_val); |
||||
// PHP would accept scientific notation here, but we're going to be a
|
||||
// little more discerning and only accept pure integers.
|
||||
bool ok = buftoint64(buf, buf + Z_STRLEN_P(php_val), i64); |
||||
if (!ok) { |
||||
throw_conversion_exception("integer", php_val); |
||||
} |
||||
return ok; |
||||
} |
||||
default: |
||||
throw_conversion_exception("integer", php_val); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
static bool to_double(zval *php_val, double *dbl) { |
||||
switch (Z_TYPE_P(php_val)) { |
||||
case IS_LONG: |
||||
*dbl = Z_LVAL_P(php_val); |
||||
return true; |
||||
case IS_DOUBLE: |
||||
*dbl = Z_DVAL_P(php_val); |
||||
return true; |
||||
case IS_STRING: { |
||||
zend_long lval; |
||||
switch (is_numeric_string(Z_STRVAL_P(php_val), Z_STRLEN_P(php_val), &lval, |
||||
dbl, false)) { |
||||
case IS_LONG: |
||||
*dbl = lval; |
||||
return true; |
||||
case IS_DOUBLE: |
||||
return true; |
||||
default: |
||||
goto fail; |
||||
} |
||||
} |
||||
default: |
||||
fail: |
||||
throw_conversion_exception("double", php_val); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
static bool to_bool(zval* from, bool* to) { |
||||
switch (Z_TYPE_P(from)) { |
||||
case IS_TRUE: |
||||
*to = true; |
||||
return true; |
||||
case IS_FALSE: |
||||
*to = false; |
||||
return true; |
||||
case IS_LONG: |
||||
*to = (Z_LVAL_P(from) != 0); |
||||
return true; |
||||
case IS_DOUBLE: |
||||
*to = (Z_LVAL_P(from) != 0); |
||||
return true; |
||||
case IS_STRING: |
||||
if (Z_STRLEN_P(from) == 0 || |
||||
(Z_STRLEN_P(from) == 1 && Z_STRVAL_P(from)[0] == '0')) { |
||||
*to = false; |
||||
} else { |
||||
*to = true; |
||||
} |
||||
return true; |
||||
default: |
||||
throw_conversion_exception("bool", from); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
static bool to_string(zval* from) { |
||||
if (Z_ISREF_P(from)) { |
||||
ZVAL_DEREF(from); |
||||
} |
||||
|
||||
switch (Z_TYPE_P(from)) { |
||||
case IS_STRING: |
||||
return true; |
||||
case IS_TRUE: |
||||
case IS_FALSE: |
||||
case IS_LONG: |
||||
case IS_DOUBLE: { |
||||
zval tmp; |
||||
zend_make_printable_zval(from, &tmp); |
||||
ZVAL_COPY_VALUE(from, &tmp); |
||||
return true; |
||||
} |
||||
default: |
||||
throw_conversion_exception("string", from); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type, |
||||
const Descriptor *desc, upb_arena *arena) { |
||||
int64_t i64; |
||||
|
||||
if (Z_ISREF_P(php_val)) { |
||||
ZVAL_DEREF(php_val); |
||||
} |
||||
|
||||
switch (type) { |
||||
case UPB_TYPE_INT64: |
||||
return Convert_PhpToInt64(php_val, &upb_val->int64_val); |
||||
case UPB_TYPE_INT32: |
||||
case UPB_TYPE_ENUM: |
||||
if (!Convert_PhpToInt64(php_val, &i64)) { |
||||
return false; |
||||
} |
||||
upb_val->int32_val = i64; |
||||
return true; |
||||
case UPB_TYPE_UINT64: |
||||
if (!Convert_PhpToInt64(php_val, &i64)) { |
||||
return false; |
||||
} |
||||
upb_val->uint64_val = i64; |
||||
return true; |
||||
case UPB_TYPE_UINT32: |
||||
if (!Convert_PhpToInt64(php_val, &i64)) { |
||||
return false; |
||||
} |
||||
upb_val->uint32_val = i64; |
||||
return true; |
||||
case UPB_TYPE_DOUBLE: |
||||
return to_double(php_val, &upb_val->double_val); |
||||
case UPB_TYPE_FLOAT: |
||||
if (!to_double(php_val, &upb_val->double_val)) return false; |
||||
upb_val->float_val = upb_val->double_val; |
||||
return true; |
||||
case UPB_TYPE_BOOL: |
||||
return to_bool(php_val, &upb_val->bool_val); |
||||
case UPB_TYPE_STRING: |
||||
case UPB_TYPE_BYTES: { |
||||
char *ptr; |
||||
size_t size; |
||||
|
||||
if (!to_string(php_val)) return false; |
||||
|
||||
size = Z_STRLEN_P(php_val); |
||||
|
||||
// If arena is NULL we reference the input zval.
|
||||
// The resulting upb_strview will only be value while the zval is alive.
|
||||
if (arena) { |
||||
ptr = upb_arena_malloc(arena, size); |
||||
memcpy(ptr, Z_STRVAL_P(php_val), size); |
||||
} else { |
||||
ptr = Z_STRVAL_P(php_val); |
||||
} |
||||
|
||||
upb_val->str_val = upb_strview_make(ptr, size); |
||||
return true; |
||||
} |
||||
case UPB_TYPE_MESSAGE: |
||||
PBPHP_ASSERT(desc); |
||||
return Message_GetUpbMessage(php_val, desc, arena, |
||||
(upb_msg **)&upb_val->msg_val); |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type, |
||||
const Descriptor *desc, zval *arena) { |
||||
switch (type) { |
||||
case UPB_TYPE_INT64: |
||||
#if SIZEOF_ZEND_LONG == 8 |
||||
ZVAL_LONG(php_val, upb_val.int64_val); |
||||
#else |
||||
{ |
||||
char buf[20]; |
||||
int size = sprintf(buf, "%lld", upb_val.int64_val); |
||||
ZVAL_NEW_STR(php_val, zend_string_init(buf, size, 0)); |
||||
} |
||||
#endif |
||||
break; |
||||
case UPB_TYPE_UINT64: |
||||
#if SIZEOF_ZEND_LONG == 8 |
||||
ZVAL_LONG(php_val, upb_val.uint64_val); |
||||
#else |
||||
{ |
||||
char buf[20]; |
||||
int size = sprintf(buf, "%lld", (int64_t)upb_val.uint64_val); |
||||
ZVAL_NEW_STR(php_val, zend_string_init(buf, size, 0)); |
||||
} |
||||
#endif |
||||
break; |
||||
case UPB_TYPE_INT32: |
||||
case UPB_TYPE_ENUM: |
||||
ZVAL_LONG(php_val, upb_val.int32_val); |
||||
break; |
||||
case UPB_TYPE_UINT32: { |
||||
// Sign-extend for consistency between 32/64-bit builds.
|
||||
zend_long val = (int32_t)upb_val.uint32_val; |
||||
ZVAL_LONG(php_val, val); |
||||
break; |
||||
} |
||||
case UPB_TYPE_DOUBLE: |
||||
ZVAL_DOUBLE(php_val, upb_val.double_val); |
||||
break; |
||||
case UPB_TYPE_FLOAT: |
||||
ZVAL_DOUBLE(php_val, upb_val.float_val); |
||||
break; |
||||
case UPB_TYPE_BOOL: |
||||
ZVAL_BOOL(php_val, upb_val.bool_val); |
||||
break; |
||||
case UPB_TYPE_STRING: |
||||
case UPB_TYPE_BYTES: { |
||||
upb_strview str = upb_val.str_val; |
||||
ZVAL_NEW_STR(php_val, zend_string_init(str.data, str.size, 0)); |
||||
break; |
||||
} |
||||
case UPB_TYPE_MESSAGE: |
||||
PBPHP_ASSERT(desc); |
||||
Message_GetPhpWrapper(php_val, desc, (upb_msg*)upb_val.msg_val, arena); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val, |
||||
upb_fieldtype_t type, const Descriptor *desc, |
||||
upb_arena *arena) { |
||||
const upb_msgdef *subm = desc ? desc->msgdef : NULL; |
||||
if (subm && upb_msgdef_iswrapper(subm) && Z_TYPE_P(val) != IS_OBJECT) { |
||||
// Assigning a scalar to a wrapper-typed value. We will automatically wrap
|
||||
// the value, so the user doesn't need to create a FooWrapper(['value': X])
|
||||
// message manually.
|
||||
upb_msg *wrapper = upb_msg_new(subm, arena); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(subm, 1); |
||||
upb_fieldtype_t type_f = upb_fielddef_type(val_f); |
||||
upb_msgval msgval; |
||||
if (!Convert_PhpToUpb(val, &msgval, type_f, NULL, arena)) return false; |
||||
upb_msg_set(wrapper, val_f, msgval, arena); |
||||
upb_val->msg_val = wrapper; |
||||
return true; |
||||
} else { |
||||
// Convert_PhpToUpb doesn't auto-construct messages. This means that we only
|
||||
// allow:
|
||||
// ['foo_submsg': new Foo(['a' => 1])]
|
||||
// not:
|
||||
// ['foo_submsg': ['a' => 1]]
|
||||
return Convert_PhpToUpb(val, upb_val, type, desc, arena); |
||||
} |
||||
} |
||||
|
||||
void Convert_ModuleInit(void) { |
||||
const char *prefix_name = "TYPE_URL_PREFIX"; |
||||
zend_class_entry class_type; |
||||
|
||||
INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBUtil", |
||||
util_methods); |
||||
GPBUtil_class_entry = zend_register_internal_class(&class_type); |
||||
|
||||
zend_declare_class_constant_string(GPBUtil_class_entry, prefix_name, |
||||
strlen(prefix_name), |
||||
"type.googleapis.com/"); |
||||
} |
@ -0,0 +1,73 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_CONVERT_H_ |
||||
#define PHP_PROTOBUF_CONVERT_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
#include "def.h" |
||||
|
||||
upb_fieldtype_t pbphp_dtype_to_type(upb_descriptortype_t type); |
||||
|
||||
// Converts |php_val| to an int64_t. Returns false if the value cannot be
|
||||
// converted.
|
||||
bool Convert_PhpToInt64(const zval *php_val, int64_t *i64); |
||||
|
||||
// Converts |php_val| to a upb_msgval according to |type|. If type is
|
||||
// UPB_TYPE_MESSAGE, then |desc| must be the Descriptor for this message type.
|
||||
// If type is string, message, or bytes, then |arena| will be used to copy
|
||||
// string data or fuse this arena to the given message's arena.
|
||||
bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type, |
||||
const Descriptor *desc, upb_arena *arena); |
||||
|
||||
// Similar to Convert_PhpToUpb, but supports automatically wrapping the wrapper
|
||||
// types if a primitive is specified:
|
||||
//
|
||||
// 5 -> Int64Wrapper(value=5)
|
||||
//
|
||||
// We currently allow this implicit conversion in initializers, but not for
|
||||
// assignment.
|
||||
bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val, |
||||
upb_fieldtype_t type, const Descriptor *desc, |
||||
upb_arena *arena); |
||||
|
||||
// Converts |upb_val| to a PHP zval according to |type|. This may involve
|
||||
// creating a PHP wrapper object. If type == UPB_TYPE_MESSAGE, then |desc| must
|
||||
// be the Descriptor for this message type. Any newly created wrapper object
|
||||
// will reference |arena|.
|
||||
void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type, |
||||
const Descriptor *desc, zval *arena); |
||||
|
||||
// Registers the GPBUtil class.
|
||||
void Convert_ModuleInit(void); |
||||
|
||||
#endif // PHP_PROTOBUF_CONVERT_H_
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,69 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_DEF_H_ |
||||
#define PHP_PROTOBUF_DEF_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// Initializes the Def module, which defines all of the descriptor classes.
|
||||
void Def_ModuleInit(); |
||||
|
||||
// Creates a new DescriptorPool to wrap the given symtab. The DescriptorPool
|
||||
// takes ownership of the given symtab. If symtab is NULL, the DescriptorPool
|
||||
// will create an empty symtab instead.
|
||||
void DescriptorPool_CreateWithSymbolTable(zval *zv, upb_symtab *symtab); |
||||
|
||||
// Given a zval representing a DescriptorPool, steals and returns its symtab,
|
||||
// which is now owned by the caller.
|
||||
upb_symtab *DescriptorPool_Steal(zval *zv); |
||||
|
||||
upb_symtab *DescriptorPool_GetSymbolTable(); |
||||
|
||||
typedef struct Descriptor { |
||||
zend_object std; |
||||
const upb_msgdef *msgdef; |
||||
zend_class_entry *class_entry; |
||||
} Descriptor; |
||||
|
||||
// Gets or creates a PHP Descriptor object for a |ce| and stores it in |val|.
|
||||
// If this is not a protobuf generated class, |val| will be set to null.
|
||||
void Descriptor_FromClassEntry(zval *val, zend_class_entry *ce); |
||||
|
||||
// Gets or creates a Descriptor* for the given class entry, upb_msgdef, or
|
||||
// upb_fielddef. The returned Descriptor* will live for the entire request,
|
||||
// so no ref is necessary to keep it alive.
|
||||
Descriptor* Descriptor_GetFromClassEntry(zend_class_entry *ce); |
||||
Descriptor* Descriptor_GetFromMessageDef(const upb_msgdef *m); |
||||
Descriptor* Descriptor_GetFromFieldDef(const upb_fielddef *f); |
||||
|
||||
#endif // PHP_PROTOBUF_DEF_H_
|
@ -0,0 +1,62 @@ |
||||
<?php |
||||
|
||||
$cwd = dirname($argv[0]) . "/../../../src"; |
||||
chdir($cwd); |
||||
|
||||
$cmd = "grep -r -l 'Generated by the protocol buffer' * | grep 'php$' | grep -v Internal"; |
||||
$handle = popen($cmd, 'r'); |
||||
$filenames = explode("\n", stream_get_contents($handle)); |
||||
array_pop($filenames); // empty string after last '\n' |
||||
$filenames[] = "Google/Protobuf/DescriptorPool.php"; |
||||
$output = "../ext/google/protobuf2/bundled_php.c"; |
||||
|
||||
function stripSuffix($str, $suffix) { |
||||
return substr($str, 0, strlen($str) - strlen($suffix)); |
||||
} |
||||
|
||||
function toClassName($filename) { |
||||
# Google/Protobuf/BoolValue.php -> Google\\Protobuf\\BoolValue |
||||
$ret = stripSuffix($filename, ".php"); |
||||
return str_replace("/", "\\\\", $ret); |
||||
} |
||||
|
||||
function toCSymbolName($filename) { |
||||
# Google/Protobuf/BoolValue.php -> Google__Protobuf__BoolValue |
||||
$ret = stripSuffix($filename, ".php"); |
||||
return str_replace("/", "__", $ret); |
||||
} |
||||
|
||||
$f = fopen($output, "w"); |
||||
|
||||
fwrite($f, "#include \"bundled_php.h\"\n"); |
||||
fwrite($f, "#include \"stdlib.h\"\n"); |
||||
|
||||
foreach ($filenames as $filename) { |
||||
print("Reading $filename...\n"); |
||||
$contents = file_get_contents($filename); |
||||
$contents = substr($contents, 5); // Strip <?php |
||||
$c_symbol_name = toCSymbolName($filename); |
||||
fwrite($f, "static const char {$c_symbol_name}[] = {"); |
||||
for ($i = 0; $i < strlen($contents); $i++) { |
||||
if ($i % 10 == 0) { |
||||
fwrite($f, "\n"); |
||||
} |
||||
fprintf($f, " 0x%02x,", ord($contents[$i])); |
||||
} |
||||
fwrite($f, "0};\n"); |
||||
} |
||||
|
||||
fwrite($f, "static BundledPhp_File php[] = {\n"); |
||||
foreach ($filenames as $filename) { |
||||
$class_name = toClassName($filename); |
||||
$c_symbol_name = toCSymbolName($filename); |
||||
fwrite($f, " {\"$class_name\", $c_symbol_name},\n"); |
||||
} |
||||
|
||||
fwrite($f, " {NULL, NULL}\n"); |
||||
fwrite($f, "};\n"); |
||||
fwrite($f, "BundledPhp_File *bundled_files = &php[0];\n"); |
||||
fclose($f); |
||||
|
||||
print("Wrote $output\n"); |
||||
?> |
@ -0,0 +1,590 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "map.h" |
||||
|
||||
#include <Zend/zend_API.h> |
||||
#include <Zend/zend_interfaces.h> |
||||
|
||||
#include <ext/spl/spl_iterators.h> |
||||
|
||||
#include "arena.h" |
||||
#include "convert.h" |
||||
#include "php-upb.h" |
||||
#include "protobuf.h" |
||||
|
||||
static void MapFieldIter_make(zval *val, zval *map_field); |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// MapField
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval arena; |
||||
upb_map *map; |
||||
upb_fieldtype_t key_type; |
||||
upb_fieldtype_t val_type; |
||||
const Descriptor* desc; // When values are messages.
|
||||
} MapField; |
||||
|
||||
zend_class_entry *MapField_class_entry; |
||||
static zend_object_handlers MapField_object_handlers; |
||||
|
||||
// PHP Object Handlers /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* MapField_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new MapField |
||||
* object. |
||||
*/ |
||||
static zend_object* MapField_create(zend_class_entry *class_type) { |
||||
MapField *intern = emalloc(sizeof(MapField)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &MapField_object_handlers; |
||||
Arena_Init(&intern->arena); |
||||
intern->map = NULL; |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* MapField_dtor() |
||||
* |
||||
* Object handler to destroy a MapField. This releases all resources |
||||
* associated with the message. Note that it is possible to access a destroyed |
||||
* object from PHP in rare cases. |
||||
*/ |
||||
static void MapField_destructor(zend_object* obj) { |
||||
MapField* intern = (MapField*)obj; |
||||
ObjCache_Delete(intern->map); |
||||
zval_ptr_dtor(&intern->arena); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
static zval *Map_GetPropertyPtrPtr(zval *object, zval *member, int type, |
||||
void **cache_slot) { |
||||
return NULL; // We don't offer direct references to our properties.
|
||||
} |
||||
|
||||
static HashTable *map_get_properties(zval *object TSRMLS_DC) { |
||||
return NULL; // We do not have a properties table.
|
||||
} |
||||
|
||||
// C Functions from map.h //////////////////////////////////////////////////////
|
||||
|
||||
// These are documented in the header file.
|
||||
|
||||
void MapField_GetPhpWrapper(zval *val, upb_map *map, const upb_fielddef *f, |
||||
zval *arena) { |
||||
if (!map) { |
||||
ZVAL_NULL(val); |
||||
return; |
||||
} |
||||
|
||||
if (!ObjCache_Get(map, val)) { |
||||
const upb_msgdef *ent = upb_fielddef_msgsubdef(f); |
||||
const upb_fielddef *key_f = upb_msgdef_itof(ent, 1); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(ent, 2); |
||||
MapField *intern = emalloc(sizeof(MapField)); |
||||
zend_object_std_init(&intern->std, MapField_class_entry); |
||||
intern->std.handlers = &MapField_object_handlers; |
||||
ZVAL_COPY(&intern->arena, arena); |
||||
intern->map = map; |
||||
intern->key_type = upb_fielddef_type(key_f); |
||||
intern->val_type = upb_fielddef_type(val_f); |
||||
intern->desc = Descriptor_GetFromFieldDef(val_f); |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
ObjCache_Add(intern->map, &intern->std); |
||||
ZVAL_OBJ(val, &intern->std); |
||||
} |
||||
} |
||||
|
||||
upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena) { |
||||
const upb_msgdef *ent = upb_fielddef_msgsubdef(f); |
||||
const upb_fielddef *key_f = upb_msgdef_itof(ent, 1); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(ent, 2); |
||||
upb_fieldtype_t key_type = upb_fielddef_type(key_f); |
||||
upb_fieldtype_t val_type = upb_fielddef_type(val_f); |
||||
const Descriptor *desc = Descriptor_GetFromFieldDef(val_f); |
||||
|
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_ARRAY) { |
||||
upb_map *map = upb_map_new(arena, key_type, val_type); |
||||
HashTable *table = HASH_OF(val); |
||||
HashPosition pos; |
||||
|
||||
zend_hash_internal_pointer_reset_ex(table, &pos); |
||||
|
||||
while (true) { |
||||
zval php_key; |
||||
zval *php_val; |
||||
upb_msgval upb_key; |
||||
upb_msgval upb_val; |
||||
|
||||
zend_hash_get_current_key_zval_ex(table, &php_key, &pos); |
||||
php_val = zend_hash_get_current_data_ex(table, &pos); |
||||
|
||||
if (!php_val) return map; |
||||
|
||||
if (!Convert_PhpToUpb(&php_key, &upb_key, key_type, NULL, arena) || |
||||
!Convert_PhpToUpbAutoWrap(php_val, &upb_val, val_type, desc, arena)) { |
||||
return NULL; |
||||
} |
||||
|
||||
upb_map_set(map, upb_key, upb_val, arena); |
||||
zend_hash_move_forward_ex(table, &pos); |
||||
zval_dtor(&php_key); |
||||
} |
||||
} else if (Z_TYPE_P(val) == IS_OBJECT && |
||||
Z_OBJCE_P(val) == MapField_class_entry) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(val); |
||||
|
||||
if (intern->key_type != key_type || intern->val_type != val_type || |
||||
intern->desc != desc) { |
||||
php_error_docref(NULL, E_USER_ERROR, "Wrong type for this map field."); |
||||
return NULL; |
||||
} |
||||
|
||||
upb_arena_fuse(arena, Arena_Get(&intern->arena)); |
||||
return intern->map; |
||||
} else { |
||||
php_error_docref(NULL, E_USER_ERROR, "Must be a map"); |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
// MapField PHP methods ////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* MapField::__construct() |
||||
* |
||||
* Constructs an instance of MapField. |
||||
* @param long Key type. |
||||
* @param long Value type. |
||||
* @param string Message/Enum class (message/enum value types only). |
||||
*/ |
||||
PHP_METHOD(MapField, __construct) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zend_long key_type, val_type; |
||||
zend_class_entry* klass = NULL; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|C", &key_type, &val_type, |
||||
&klass) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
intern->key_type = pbphp_dtype_to_type(key_type); |
||||
intern->val_type = pbphp_dtype_to_type(val_type); |
||||
intern->desc = Descriptor_GetFromClassEntry(klass); |
||||
|
||||
// Check that the key type is an allowed type.
|
||||
switch (intern->key_type) { |
||||
case UPB_TYPE_INT32: |
||||
case UPB_TYPE_INT64: |
||||
case UPB_TYPE_UINT32: |
||||
case UPB_TYPE_UINT64: |
||||
case UPB_TYPE_BOOL: |
||||
case UPB_TYPE_STRING: |
||||
case UPB_TYPE_BYTES: |
||||
// These are OK.
|
||||
break; |
||||
default: |
||||
zend_error(E_USER_ERROR, "Invalid key type for map."); |
||||
} |
||||
|
||||
if (intern->val_type == UPB_TYPE_MESSAGE && klass == NULL) { |
||||
php_error_docref(NULL, E_USER_ERROR, |
||||
"Message/enum type must have concrete class."); |
||||
return; |
||||
} |
||||
|
||||
intern->map = upb_map_new(arena, intern->key_type, intern->val_type); |
||||
ObjCache_Add(intern->map, &intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::offsetExists() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* isset($map[$idx]); |
||||
* empty($map[$idx]); |
||||
* |
||||
* @param long The index to be checked. |
||||
* @return bool True if the element at the given index exists. |
||||
*/ |
||||
PHP_METHOD(MapField, offsetExists) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
zval *key; |
||||
upb_msgval upb_key; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS || |
||||
!Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) { |
||||
return; |
||||
} |
||||
|
||||
RETURN_BOOL(upb_map_get(intern->map, upb_key, NULL)); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::offsetGet() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* $x = $map[$idx]; |
||||
* |
||||
* @param long The index of the element to be fetched. |
||||
* @return object The stored element at given index. |
||||
* @exception Invalid type for index. |
||||
* @exception Non-existing index. |
||||
*/ |
||||
PHP_METHOD(MapField, offsetGet) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
zval *key; |
||||
zval ret; |
||||
upb_msgval upb_key, upb_val; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS || |
||||
!Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) { |
||||
return; |
||||
} |
||||
|
||||
if (!upb_map_get(intern->map, upb_key, &upb_val)) { |
||||
zend_error(E_USER_ERROR, "Given key doesn't exist."); |
||||
return; |
||||
} |
||||
|
||||
Convert_UpbToPhp(upb_val, &ret, intern->val_type, intern->desc, &intern->arena); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::offsetSet() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* $map[$idx] = $x; |
||||
* |
||||
* @param long The index of the element to be assigned. |
||||
* @param object The element to be assigned. |
||||
* @exception Invalid type for index. |
||||
* @exception Non-existing index. |
||||
* @exception Incorrect type of the element. |
||||
*/ |
||||
PHP_METHOD(MapField, offsetSet) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zval *key, *val; |
||||
upb_msgval upb_key, upb_val; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &key, &val) != SUCCESS || |
||||
!Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL) || |
||||
!Convert_PhpToUpb(val, &upb_val, intern->val_type, intern->desc, arena)) { |
||||
return; |
||||
} |
||||
|
||||
upb_map_set(intern->map, upb_key, upb_val, arena); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::offsetUnset() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* unset($map[$idx]); |
||||
* |
||||
* @param long The index of the element to be removed. |
||||
* @exception Invalid type for index. |
||||
* @exception The element to be removed is not at the end of the MapField. |
||||
*/ |
||||
PHP_METHOD(MapField, offsetUnset) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
zval *key; |
||||
upb_msgval upb_key; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS || |
||||
!Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL)) { |
||||
return; |
||||
} |
||||
|
||||
upb_map_delete(intern->map, upb_key); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::count() |
||||
* |
||||
* Implements the Countable interface. Invoked when PHP code calls: |
||||
* |
||||
* $len = count($map); |
||||
* Return the number of stored elements. |
||||
* This will also be called for: count($map) |
||||
* @return long The number of stored elements. |
||||
*/ |
||||
PHP_METHOD(MapField, count) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
|
||||
if (zend_parse_parameters_none() == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
RETURN_LONG(upb_map_size(intern->map)); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::getIterator() |
||||
* |
||||
* Implements the IteratorAggregate interface. Invoked when PHP code calls: |
||||
* |
||||
* foreach ($arr) {} |
||||
* |
||||
* @return object Beginning iterator. |
||||
*/ |
||||
PHP_METHOD(MapField, getIterator) { |
||||
zval ret; |
||||
MapFieldIter_make(&ret, getThis()); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1) |
||||
ZEND_ARG_INFO(0, index) |
||||
ZEND_END_ARG_INFO() |
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2) |
||||
ZEND_ARG_INFO(0, index) |
||||
ZEND_ARG_INFO(0, newval) |
||||
ZEND_END_ARG_INFO() |
||||
|
||||
ZEND_BEGIN_ARG_INFO(arginfo_void, 0) |
||||
ZEND_END_ARG_INFO() |
||||
|
||||
static zend_function_entry MapField_methods[] = { |
||||
PHP_ME(MapField, __construct, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapField, offsetGet, arginfo_offsetGet, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapField, count, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapField, getIterator, arginfo_void, ZEND_ACC_PUBLIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// MapFieldIter
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval map_field; |
||||
size_t position; |
||||
} MapFieldIter; |
||||
|
||||
zend_class_entry *MapFieldIter_class_entry; |
||||
static zend_object_handlers MapFieldIter_object_handlers; |
||||
|
||||
/**
|
||||
* MapFieldIter_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new MapFieldIter |
||||
* object. |
||||
*/ |
||||
zend_object* MapFieldIter_create(zend_class_entry *class_type) { |
||||
MapFieldIter *intern = emalloc(sizeof(MapFieldIter)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &MapFieldIter_object_handlers; |
||||
ZVAL_NULL(&intern->map_field); |
||||
intern->position = 0; |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter_dtor() |
||||
* |
||||
* Object handler to destroy a MapFieldIter. This releases all resources |
||||
* associated with the message. Note that it is possible to access a destroyed |
||||
* object from PHP in rare cases. |
||||
*/ |
||||
static void map_field_iter_dtor(zend_object* obj) { |
||||
MapFieldIter* intern = (MapFieldIter*)obj; |
||||
zval_ptr_dtor(&intern->map_field); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter_make() |
||||
* |
||||
* Function to create a MapFieldIter directly from C. |
||||
*/ |
||||
static void MapFieldIter_make(zval *val, zval *map_field) { |
||||
MapFieldIter *iter; |
||||
ZVAL_OBJ(val, |
||||
MapFieldIter_class_entry->create_object(MapFieldIter_class_entry)); |
||||
iter = (MapFieldIter*)Z_OBJ_P(val); |
||||
ZVAL_COPY(&iter->map_field, map_field); |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PHP MapFieldIter Methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* When a user writes: |
||||
* |
||||
* foreach($arr as $key => $val) {} |
||||
* |
||||
* PHP translates this into: |
||||
* |
||||
* $iter = $arr->getIterator(); |
||||
* for ($iter->rewind(); $iter->valid(); $iter->next()) { |
||||
* $key = $iter->key(); |
||||
* $val = $iter->current(); |
||||
* } |
||||
*/ |
||||
|
||||
/**
|
||||
* MapFieldIter::rewind() |
||||
* |
||||
* Implements the Iterator interface. Sets the iterator to the first element. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, rewind) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *map_field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
intern->position = UPB_MAP_BEGIN; |
||||
upb_mapiter_next(map_field->map, &intern->position); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter::current() |
||||
* |
||||
* Implements the Iterator interface. Returns the current value. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, current) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
upb_msgval upb_val = upb_mapiter_value(field->map, intern->position); |
||||
zval ret; |
||||
Convert_UpbToPhp(upb_val, &ret, field->val_type, field->desc, &field->arena); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter::key() |
||||
* |
||||
* Implements the Iterator interface. Returns the current key. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, key) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
upb_msgval upb_key = upb_mapiter_key(field->map, intern->position); |
||||
zval ret; |
||||
Convert_UpbToPhp(upb_key, &ret, field->key_type, NULL, NULL); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter::next() |
||||
* |
||||
* Implements the Iterator interface. Advances to the next element. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, next) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
upb_mapiter_next(field->map, &intern->position); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter::valid() |
||||
* |
||||
* Implements the Iterator interface. Returns true if this is a valid element. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, valid) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
bool done = upb_mapiter_done(field->map, intern->position); |
||||
RETURN_BOOL(!done); |
||||
} |
||||
|
||||
static zend_function_entry map_field_iter_methods[] = { |
||||
PHP_ME(MapFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module init.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Map_ModuleInit() |
||||
* |
||||
* Called when the C extension is loaded to register all types. |
||||
*/ |
||||
|
||||
void Map_ModuleInit() { |
||||
zend_class_entry tmp_ce; |
||||
zend_object_handlers *h; |
||||
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapField", |
||||
MapField_methods); |
||||
|
||||
MapField_class_entry = zend_register_internal_class(&tmp_ce); |
||||
zend_class_implements(MapField_class_entry, 3, spl_ce_ArrayAccess, |
||||
zend_ce_aggregate, spl_ce_Countable); |
||||
MapField_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
MapField_class_entry->create_object = MapField_create; |
||||
|
||||
h = &MapField_object_handlers; |
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = MapField_destructor; |
||||
h->get_properties = map_get_properties; |
||||
h->get_property_ptr_ptr = Map_GetPropertyPtrPtr; |
||||
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapFieldIter", |
||||
map_field_iter_methods); |
||||
|
||||
MapFieldIter_class_entry = zend_register_internal_class(&tmp_ce); |
||||
zend_class_implements(MapFieldIter_class_entry, 1, zend_ce_iterator); |
||||
MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
MapFieldIter_class_entry->create_object = MapFieldIter_create; |
||||
|
||||
h = &MapFieldIter_object_handlers; |
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = map_field_iter_dtor; |
||||
} |
@ -0,0 +1,60 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_MAP_H_ |
||||
#define PHP_PROTOBUF_MAP_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
void Map_ModuleInit(); |
||||
|
||||
// Gets a upb_map* for the PHP object |val|:
|
||||
// * If |val| is a RepeatedField object, we first check its type and verify
|
||||
// that that the elements have the correct type for |f|. If so, we return the
|
||||
// wrapped upb_map*. We also make sure that this map's arena is fused to
|
||||
// |arena|, so the returned upb_map is guaranteed to live as long as
|
||||
// |arena|.
|
||||
// * If |val| is a PHP Map, we attempt to create a new upb_map using
|
||||
// |arena| and add all of the PHP elements to it.
|
||||
//
|
||||
// If an error occurs, we raise a PHP error and return NULL.
|
||||
upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena); |
||||
|
||||
// Creates a PHP MapField object for the given upb_map* and |f| and returns it
|
||||
// in |val|. The PHP object will keep a reference to this |arena| to ensure the
|
||||
// underlying array data stays alive.
|
||||
//
|
||||
// If |map| is NULL, this will return a PHP null object.
|
||||
void MapField_GetPhpWrapper(zval *val, upb_map *arr, const upb_fielddef *f, |
||||
zval *arena); |
||||
|
||||
#endif // PHP_PROTOBUF_MAP_H_
|
@ -0,0 +1,841 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "message.h" |
||||
|
||||
#include <inttypes.h> |
||||
#include <php.h> |
||||
#include <stdlib.h> |
||||
|
||||
// This is not self-contained: it must be after other Zend includes.
|
||||
#include <Zend/zend_exceptions.h> |
||||
|
||||
#include "arena.h" |
||||
#include "array.h" |
||||
#include "convert.h" |
||||
#include "def.h" |
||||
#include "map.h" |
||||
#include "php-upb.h" |
||||
#include "protobuf.h" |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Message
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval arena; |
||||
const Descriptor* desc; |
||||
upb_msg *msg; |
||||
} Message; |
||||
|
||||
zend_class_entry *message_ce; |
||||
static zend_object_handlers message_object_handlers; |
||||
|
||||
// PHP Object Handlers /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Message_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new Message object. |
||||
*/ |
||||
static zend_object* Message_create(zend_class_entry *class_type) { |
||||
Message *intern = emalloc(sizeof(Message)); |
||||
// XXX(haberman): verify whether we actually want to take this route.
|
||||
class_type->default_properties_count = 0; |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &message_object_handlers; |
||||
Arena_Init(&intern->arena); |
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* Message_dtor() |
||||
* |
||||
* Object handler to destroy a Message. This releases all resources associated |
||||
* with the message. Note that it is possible to access a destroyed object from |
||||
* PHP in rare cases. |
||||
*/ |
||||
static void Message_dtor(zend_object* obj) { |
||||
Message* intern = (Message*)obj; |
||||
ObjCache_Delete(intern->msg); |
||||
zval_dtor(&intern->arena); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* get_field() |
||||
* |
||||
* Helper function to look up a field given a member name (as a string). |
||||
*/ |
||||
static const upb_fielddef *get_field(Message *msg, zval *member) { |
||||
const upb_msgdef *m = msg->desc->msgdef; |
||||
const upb_fielddef *f = |
||||
upb_msgdef_ntof(m, Z_STRVAL_P(member), Z_STRLEN_P(member)); |
||||
|
||||
if (!f) { |
||||
zend_throw_exception_ex(NULL, 0, "No such property %s.", |
||||
ZSTR_VAL(msg->desc->class_entry->name)); |
||||
} |
||||
|
||||
return f; |
||||
} |
||||
|
||||
/**
|
||||
* Message_read_property() |
||||
* |
||||
* Object handler for reading a property in PHP. Called when PHP code does: |
||||
* |
||||
* $x = $message->foobar; |
||||
* |
||||
* Note that all properties of generated messages are private, so this should |
||||
* only be possible to invoke from generated code, which has accessors like: |
||||
* |
||||
* public function getOptionalInt32() |
||||
* { |
||||
* return $this->optional_int32; |
||||
* } |
||||
* |
||||
* We lookup the field and return the scalar, RepeatedField, or MapField for |
||||
* this field. |
||||
*/ |
||||
static zval *Message_read_property(zval *obj, zval *member, int type, |
||||
void **cache_slot, zval *rv) { |
||||
Message* intern = (Message*)Z_OBJ_P(obj); |
||||
const upb_fielddef *f = get_field(intern, member); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
|
||||
if (!f) return NULL; |
||||
|
||||
if (upb_fielddef_ismap(f)) { |
||||
upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena); |
||||
MapField_GetPhpWrapper(rv, msgval.map, f, &intern->arena); |
||||
} else if (upb_fielddef_isseq(f)) { |
||||
upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena); |
||||
RepeatedField_GetPhpWrapper(rv, msgval.array, f, &intern->arena); |
||||
} else { |
||||
upb_msgval msgval = upb_msg_get(intern->msg, f); |
||||
const Descriptor *subdesc = Descriptor_GetFromFieldDef(f); |
||||
Convert_UpbToPhp(msgval, rv, upb_fielddef_type(f), subdesc, &intern->arena); |
||||
} |
||||
|
||||
return rv; |
||||
} |
||||
|
||||
/**
|
||||
* Message_write_property() |
||||
* |
||||
* Object handler for writing a property in PHP. Called when PHP code does: |
||||
* |
||||
* $message->foobar = $x; |
||||
* |
||||
* Note that all properties of generated messages are private, so this should |
||||
* only be possible to invoke from generated code, which has accessors like: |
||||
* |
||||
* public function setOptionalInt32($var) |
||||
* { |
||||
* GPBUtil::checkInt32($var); |
||||
* $this->optional_int32 = $var; |
||||
* |
||||
* return $this; |
||||
* } |
||||
* |
||||
* The C extension version of checkInt32() doesn't actually check anything, so |
||||
* we perform all checking and conversion in this function. |
||||
*/ |
||||
static void Message_write_property(zval *obj, zval *member, zval *val, |
||||
void **cache_slot) { |
||||
Message* intern = (Message*)Z_OBJ_P(obj); |
||||
const upb_fielddef *f = get_field(intern, member); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
upb_msgval msgval; |
||||
|
||||
if (!f) return; |
||||
|
||||
if (upb_fielddef_ismap(f)) { |
||||
msgval.map_val = MapField_GetUpbMap(val, f, arena); |
||||
if (!msgval.map_val) return; |
||||
} else if (upb_fielddef_isseq(f)) { |
||||
msgval.array_val = RepeatedField_GetUpbArray(val, f, arena); |
||||
if (!msgval.array_val) return; |
||||
} else { |
||||
upb_fieldtype_t type = upb_fielddef_type(f); |
||||
const Descriptor *subdesc = Descriptor_GetFromFieldDef(f); |
||||
bool ok = Convert_PhpToUpb(val, &msgval, type, subdesc, arena); |
||||
if (!ok) return; |
||||
} |
||||
|
||||
upb_msg_set(intern->msg, f, msgval, arena); |
||||
} |
||||
|
||||
/**
|
||||
* Message_get_property_ptr_ptr() |
||||
* |
||||
* Object handler for the get_property_ptr_ptr event in PHP. This returns a |
||||
* reference to our internal properties. We don't support this, so we return |
||||
* NULL. |
||||
*/ |
||||
static zval *Message_get_property_ptr_ptr(zval *object, zval *member, int type, |
||||
void **cache_slot) { |
||||
return NULL; // We do not have a properties table.
|
||||
} |
||||
|
||||
/**
|
||||
* Message_get_properties() |
||||
* |
||||
* Object handler for the get_properties event in PHP. This returns a HashTable |
||||
* of our internal properties. We don't support this, so we return NULL. |
||||
*/ |
||||
static HashTable* Message_get_properties(zval* object TSRMLS_DC) { |
||||
return NULL; // We don't offer direct references to our properties.
|
||||
} |
||||
|
||||
// C Functions from message.h. /////////////////////////////////////////////////
|
||||
|
||||
// These are documented in the header file.
|
||||
|
||||
void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg, |
||||
zval *arena) { |
||||
if (!msg) { |
||||
ZVAL_NULL(val); |
||||
return; |
||||
} |
||||
|
||||
if (!ObjCache_Get(msg, val)) { |
||||
Message *intern = emalloc(sizeof(Message)); |
||||
// XXX(haberman): verify whether we actually want to take this route.
|
||||
desc->class_entry->default_properties_count = 0; |
||||
zend_object_std_init(&intern->std, desc->class_entry); |
||||
intern->std.handlers = &message_object_handlers; |
||||
ZVAL_COPY(&intern->arena, arena); |
||||
intern->desc = desc; |
||||
intern->msg = msg; |
||||
ZVAL_OBJ(val, &intern->std); |
||||
ObjCache_Add(intern->msg, &intern->std); |
||||
} |
||||
} |
||||
|
||||
bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena, |
||||
upb_msg **msg) { |
||||
PBPHP_ASSERT(desc); |
||||
|
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_NULL) { |
||||
*msg = NULL; |
||||
return true; |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_OBJECT && |
||||
instanceof_function(Z_OBJCE_P(val), desc->class_entry)) { |
||||
Message *intern = (Message*)Z_OBJ_P(val); |
||||
upb_arena_fuse(arena, Arena_Get(&intern->arena)); |
||||
*msg = intern->msg; |
||||
return true; |
||||
} else { |
||||
zend_throw_exception_ex(NULL, 0, "Given value is not an instance of %s.", |
||||
ZSTR_VAL(desc->class_entry->name)); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// Message PHP methods /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Message_InitFromPhp() |
||||
* |
||||
* Helper method to handle the initialization of a message from a PHP value, eg. |
||||
* |
||||
* $m = new TestMessage([ |
||||
* 'optional_int32' => -42, |
||||
* 'optional_bool' => true, |
||||
* 'optional_string' => 'a', |
||||
* 'optional_enum' => TestEnum::ONE, |
||||
* 'optional_message' => new Sub([ |
||||
* 'a' => 33 |
||||
* ]), |
||||
* 'repeated_int32' => [-42, -52], |
||||
* 'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE], |
||||
* 'repeated_message' => [new Sub(['a' => 34]), |
||||
* new Sub(['a' => 35])], |
||||
* 'map_int32_int32' => [-62 => -62], |
||||
* 'map_int32_enum' => [1 => TestEnum::ONE], |
||||
* 'map_int32_message' => [1 => new Sub(['a' => 36])], |
||||
* ]); |
||||
* |
||||
* The initializer must be an array. |
||||
*/ |
||||
bool Message_InitFromPhp(upb_msg *msg, const upb_msgdef *m, zval *init, |
||||
upb_arena *arena) { |
||||
HashTable* table = HASH_OF(init); |
||||
HashPosition pos; |
||||
|
||||
if (Z_ISREF_P(init)) { |
||||
ZVAL_DEREF(init); |
||||
} |
||||
|
||||
if (Z_TYPE_P(init) != IS_ARRAY) { |
||||
zend_throw_exception_ex(NULL, 0, |
||||
"Initializer for a message %s must be an array.", |
||||
upb_msgdef_fullname(m)); |
||||
return false; |
||||
} |
||||
|
||||
zend_hash_internal_pointer_reset_ex(table, &pos); |
||||
|
||||
while (true) { // Iterate over key/value pairs.
|
||||
zval key; |
||||
zval *val; |
||||
const upb_fielddef *f; |
||||
upb_msgval msgval; |
||||
|
||||
zend_hash_get_current_key_zval_ex(table, &key, &pos); |
||||
val = zend_hash_get_current_data_ex(table, &pos); |
||||
|
||||
if (!val) return true; // Finished iteration.
|
||||
|
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
f = upb_msgdef_ntof(m, Z_STRVAL_P(&key), Z_STRLEN_P(&key)); |
||||
|
||||
if (!f) { |
||||
zend_throw_exception_ex(NULL, 0, |
||||
"No such field %s", Z_STRVAL_P(&key)); |
||||
return false; |
||||
} |
||||
|
||||
if (upb_fielddef_ismap(f)) { |
||||
msgval.map_val = MapField_GetUpbMap(val, f, arena); |
||||
if (!msgval.map_val) return false; |
||||
} else if (upb_fielddef_isseq(f)) { |
||||
msgval.array_val = RepeatedField_GetUpbArray(val, f, arena); |
||||
if (!msgval.array_val) return false; |
||||
} else { |
||||
const Descriptor *desc = Descriptor_GetFromFieldDef(f); |
||||
upb_fieldtype_t type = upb_fielddef_type(f); |
||||
if (!Convert_PhpToUpbAutoWrap(val, &msgval, type, desc, arena)) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
upb_msg_set(msg, f, msgval, arena); |
||||
zend_hash_move_forward_ex(table, &pos); |
||||
zval_dtor(&key); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::__construct() |
||||
* |
||||
* Constructor for Message. |
||||
* @param array Map of initial values ['k' = val] |
||||
*/ |
||||
PHP_METHOD(Message, __construct) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis())); |
||||
const upb_msgdef *msgdef = desc->msgdef; |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zval *init_arr = NULL; |
||||
|
||||
intern->desc = desc; |
||||
intern->msg = upb_msg_new(msgdef, arena); |
||||
ObjCache_Add(intern->msg, &intern->std); |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
if (init_arr) { |
||||
Message_InitFromPhp(intern->msg, desc->msgdef, init_arr, arena); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::discardUnknownFields() |
||||
* |
||||
* Discards any unknown fields for this message or any submessages. |
||||
*/ |
||||
PHP_METHOD(Message, discardUnknownFields) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
upb_msg_discardunknown(intern->msg, intern->desc->msgdef, 64); |
||||
} |
||||
|
||||
/**
|
||||
* Message::clear() |
||||
* |
||||
* Clears all fields of this message. |
||||
*/ |
||||
PHP_METHOD(Message, clear) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
upb_msg_clear(intern->msg, intern->desc->msgdef); |
||||
} |
||||
|
||||
/**
|
||||
* Message::mergeFrom() |
||||
* |
||||
* Merges from the given message, which must be of the same class as us. |
||||
* @param object Message to merge from. |
||||
*/ |
||||
PHP_METHOD(Message, mergeFrom) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
Message* from; |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef); |
||||
zval* value; |
||||
char *pb; |
||||
size_t size; |
||||
bool ok; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &value, |
||||
intern->desc->class_entry) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
from = (Message*)Z_OBJ_P(value); |
||||
|
||||
// Should be guaranteed since we passed the class type to
|
||||
// zend_parse_parameters().
|
||||
PBPHP_ASSERT(from->desc == intern->desc); |
||||
|
||||
// TODO(haberman): use a temp arena for this once we can make upb_decode()
|
||||
// copy strings.
|
||||
pb = upb_encode(from->msg, l, arena, &size); |
||||
|
||||
if (!pb) { |
||||
zend_throw_exception_ex(NULL, 0, "Max nesting exceeded"); |
||||
return; |
||||
} |
||||
|
||||
ok = upb_decode(pb, size, intern->msg, l, arena); |
||||
PBPHP_ASSERT(ok); |
||||
} |
||||
|
||||
/**
|
||||
* Message::mergeFromString() |
||||
* |
||||
* Merges from the given string. |
||||
* @param string Binary protobuf data to merge. |
||||
*/ |
||||
PHP_METHOD(Message, mergeFromString) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
char *data = NULL; |
||||
char *data_copy = NULL; |
||||
zend_long data_len; |
||||
const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) == |
||||
FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
// TODO(haberman): avoid this copy when we can make the decoder copy.
|
||||
data_copy = upb_arena_malloc(arena, data_len); |
||||
memcpy(data_copy, data, data_len); |
||||
|
||||
if (!upb_decode(data_copy, data_len, intern->msg, l, arena)) { |
||||
zend_throw_exception_ex(NULL, 0, "Error occurred during parsing"); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::serializeToString() |
||||
* |
||||
* Serializes this message instance to protobuf data. |
||||
* @return string Serialized protobuf data. |
||||
*/ |
||||
PHP_METHOD(Message, serializeToString) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef); |
||||
upb_arena *tmp_arena = upb_arena_new(); |
||||
char *data; |
||||
size_t size; |
||||
|
||||
data = upb_encode(intern->msg, l, tmp_arena, &size); |
||||
|
||||
if (!data) { |
||||
zend_throw_exception_ex(NULL, 0, "Error occurred during serialization"); |
||||
upb_arena_free(tmp_arena); |
||||
return; |
||||
} |
||||
|
||||
RETVAL_STRINGL(data, size); |
||||
upb_arena_free(tmp_arena); |
||||
} |
||||
|
||||
/**
|
||||
* Message::mergeFromJsonString() |
||||
* |
||||
* Merges the JSON data parsed from the given string. |
||||
* @param string Serialized JSON data. |
||||
*/ |
||||
PHP_METHOD(Message, mergeFromJsonString) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
char *data = NULL; |
||||
char *data_copy = NULL; |
||||
zend_long data_len; |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
upb_status status; |
||||
zend_bool ignore_json_unknown = false; |
||||
int options = 0; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &data, &data_len, |
||||
&ignore_json_unknown) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
// TODO(haberman): avoid this copy when we can make the decoder copy.
|
||||
data_copy = upb_arena_malloc(arena, data_len + 1); |
||||
memcpy(data_copy, data, data_len); |
||||
data_copy[data_len] = '\0'; |
||||
|
||||
if (ignore_json_unknown) { |
||||
options |= UPB_JSONDEC_IGNOREUNKNOWN; |
||||
} |
||||
|
||||
upb_status_clear(&status); |
||||
if (!upb_json_decode(data_copy, data_len, intern->msg, intern->desc->msgdef, |
||||
DescriptorPool_GetSymbolTable(), options, arena, |
||||
&status)) { |
||||
zend_throw_exception_ex(NULL, 0, "Error occurred during parsing: %s", |
||||
upb_status_errmsg(&status)); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::serializeToJsonString() |
||||
* |
||||
* Serializes this object to JSON. |
||||
* @return string Serialized JSON data. |
||||
*/ |
||||
PHP_METHOD(Message, serializeToJsonString) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
size_t size; |
||||
int options = 0; |
||||
char buf[1024]; |
||||
zend_bool preserve_proto_fieldnames = false; |
||||
upb_status status; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", |
||||
&preserve_proto_fieldnames) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
if (preserve_proto_fieldnames) { |
||||
options |= UPB_JSONENC_PROTONAMES; |
||||
} |
||||
|
||||
upb_status_clear(&status); |
||||
size = upb_json_encode(intern->msg, intern->desc->msgdef, |
||||
DescriptorPool_GetSymbolTable(), options, buf, |
||||
sizeof(buf), &status); |
||||
|
||||
if (!upb_ok(&status)) { |
||||
zend_throw_exception_ex(NULL, 0, |
||||
"Error occurred during JSON serialization: %s", |
||||
upb_status_errmsg(&status)); |
||||
return; |
||||
} |
||||
|
||||
if (size >= sizeof(buf)) { |
||||
char *buf2 = malloc(size + 1); |
||||
upb_json_encode(intern->msg, intern->desc->msgdef, |
||||
DescriptorPool_GetSymbolTable(), options, buf2, size + 1, |
||||
&status); |
||||
RETVAL_STRINGL(buf2, size); |
||||
free(buf2); |
||||
} else { |
||||
RETVAL_STRINGL(buf, size); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::readWrapperValue() |
||||
* |
||||
* Returns an unboxed value for the given field. This is called from generated |
||||
* methods for wrapper fields, eg. |
||||
* |
||||
* public function getDoubleValueUnwrapped() |
||||
* { |
||||
* return $this->readWrapperValue("double_value"); |
||||
* } |
||||
* |
||||
* @return Unwrapped field value or null. |
||||
*/ |
||||
PHP_METHOD(Message, readWrapperValue) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
char* member; |
||||
const upb_fielddef *f; |
||||
zend_long size; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &member, &size) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
f = upb_msgdef_ntof(intern->desc->msgdef, member, size); |
||||
|
||||
if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) { |
||||
zend_throw_exception_ex(NULL, 0, "Message %s has no field %s", |
||||
upb_msgdef_fullname(intern->desc->msgdef), member); |
||||
return; |
||||
} |
||||
|
||||
if (upb_msg_has(intern->msg, f)) { |
||||
const upb_msg *wrapper = upb_msg_get(intern->msg, f).msg_val; |
||||
const upb_msgdef *m = upb_fielddef_msgsubdef(f); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(m, 1); |
||||
const upb_fieldtype_t val_type = upb_fielddef_type(val_f); |
||||
upb_msgval msgval = upb_msg_get(wrapper, val_f); |
||||
zval ret; |
||||
Convert_UpbToPhp(msgval, &ret, val_type, NULL, &intern->arena); |
||||
RETURN_ZVAL(&ret, 1, 0); |
||||
} else { |
||||
RETURN_NULL(); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::writeWrapperValue() |
||||
* |
||||
* Sets the given wrapper field to the given unboxed value. This is called from |
||||
* generated methods for wrapper fields, eg. |
||||
* |
||||
* |
||||
* public function setDoubleValueUnwrapped($var) |
||||
* { |
||||
* $this->writeWrapperValue("double_value", $var); |
||||
* return $this; |
||||
* } |
||||
* |
||||
* @param Unwrapped field value or null. |
||||
*/ |
||||
PHP_METHOD(Message, writeWrapperValue) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
char* member; |
||||
const upb_fielddef *f; |
||||
upb_msgval msgval; |
||||
zend_long size; |
||||
zval* val; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &member, &size, &val) == |
||||
FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
f = upb_msgdef_ntof(intern->desc->msgdef, member, size); |
||||
|
||||
if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) { |
||||
zend_throw_exception_ex(NULL, 0, "Message %s has no field %s", |
||||
upb_msgdef_fullname(intern->desc->msgdef), member); |
||||
return; |
||||
} |
||||
|
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_NULL) { |
||||
upb_msg_clearfield(intern->msg, f); |
||||
} else { |
||||
const upb_msgdef *m = upb_fielddef_msgsubdef(f); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(m, 1); |
||||
upb_fieldtype_t val_type = upb_fielddef_type(val_f); |
||||
upb_msg *wrapper; |
||||
|
||||
if (!Convert_PhpToUpb(val, &msgval, val_type, NULL, arena)) { |
||||
return; // Error is already set.
|
||||
} |
||||
|
||||
wrapper = upb_msg_mutable(intern->msg, f, arena).msg; |
||||
upb_msg_set(wrapper, val_f, msgval, arena); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::whichOneof() |
||||
* |
||||
* Given a oneof name, returns the name of the field that is set for this oneof, |
||||
* or otherwise the empty string. |
||||
* |
||||
* @return string The field name in this oneof that is currently set. |
||||
*/ |
||||
PHP_METHOD(Message, whichOneof) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
const upb_oneofdef* oneof; |
||||
const upb_fielddef* field; |
||||
char* name; |
||||
zend_long len; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &len) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
oneof = upb_msgdef_ntoo(intern->desc->msgdef, name, len); |
||||
|
||||
if (!oneof) { |
||||
zend_throw_exception_ex(NULL, 0, "Message %s has no oneof %s", |
||||
upb_msgdef_fullname(intern->desc->msgdef), name); |
||||
return; |
||||
} |
||||
|
||||
field = upb_msg_whichoneof(intern->msg, oneof); |
||||
RETURN_STRING(field ? upb_fielddef_name(field) : ""); |
||||
} |
||||
|
||||
/**
|
||||
* Message::readOneof() |
||||
* |
||||
* Returns the contents of the given oneof field, given a field number. Called |
||||
* from generated code methods such as: |
||||
* |
||||
* public function getDoubleValueOneof() |
||||
* { |
||||
* return $this->readOneof(10); |
||||
* } |
||||
* |
||||
* @return object The oneof's field value. |
||||
*/ |
||||
PHP_METHOD(Message, readOneof) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
zend_long field_num; |
||||
const upb_fielddef* f; |
||||
zval ret; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &field_num) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
f = upb_msgdef_itof(intern->desc->msgdef, field_num); |
||||
|
||||
if (!f || !upb_fielddef_realcontainingoneof(f)) { |
||||
php_error_docref(NULL, E_USER_ERROR, |
||||
"Internal error, no such oneof field %d\n", |
||||
(int)field_num); |
||||
} |
||||
|
||||
{ |
||||
upb_msgval msgval = upb_msg_get(intern->msg, f); |
||||
const Descriptor *subdesc = Descriptor_GetFromFieldDef(f); |
||||
Convert_UpbToPhp(msgval, &ret, upb_fielddef_type(f), subdesc, |
||||
&intern->arena); |
||||
} |
||||
|
||||
RETURN_ZVAL(&ret, 1, 0); |
||||
} |
||||
|
||||
/**
|
||||
* Message::writeOneof() |
||||
* |
||||
* Sets the contents of the given oneof field, given a field number. Called |
||||
* from generated code methods such as: |
||||
* |
||||
* public function setDoubleValueOneof($var) |
||||
* { |
||||
* GPBUtil::checkMessage($var, \Google\Protobuf\DoubleValue::class); |
||||
* $this->writeOneof(10, $var); |
||||
* |
||||
* return $this; |
||||
* } |
||||
* |
||||
* The C extension version of GPBUtil::check*() does nothing, so we perform |
||||
* all type checking and conversion here. |
||||
* |
||||
* @param integer The field number we are setting. |
||||
* @param object The field value we want to set. |
||||
*/ |
||||
PHP_METHOD(Message, writeOneof) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
zend_long field_num; |
||||
const upb_fielddef* f; |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
upb_msgval msgval; |
||||
zval* val; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &field_num, &val) == |
||||
FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
f = upb_msgdef_itof(intern->desc->msgdef, field_num); |
||||
|
||||
if (!Convert_PhpToUpb(val, &msgval, upb_fielddef_type(f), |
||||
Descriptor_GetFromFieldDef(f), arena)) { |
||||
return; |
||||
} |
||||
|
||||
upb_msg_set(intern->msg, f, msgval, arena); |
||||
} |
||||
|
||||
static zend_function_entry Message_methods[] = { |
||||
PHP_ME(Message, clear, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, discardUnknownFields, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, serializeToString, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, mergeFromString, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, serializeToJsonString, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, mergeFromJsonString, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, readWrapperValue, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, writeWrapperValue, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, __construct, NULL, ZEND_ACC_PROTECTED) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
/**
|
||||
* Message_ModuleInit() |
||||
* |
||||
* Called when the C extension is loaded to register all types. |
||||
*/ |
||||
void Message_ModuleInit() { |
||||
zend_class_entry tmp_ce; |
||||
zend_object_handlers *h = &message_object_handlers; |
||||
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Message", |
||||
Message_methods); |
||||
|
||||
message_ce = zend_register_internal_class(&tmp_ce); |
||||
message_ce->create_object = Message_create; |
||||
|
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = Message_dtor; |
||||
h->read_property = Message_read_property; |
||||
h->write_property = Message_write_property; |
||||
h->get_properties = Message_get_properties; |
||||
h->get_property_ptr_ptr = Message_get_property_ptr_ptr; |
||||
} |
@ -0,0 +1,59 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_MESSAGE_H_ |
||||
#define PHP_PROTOBUF_MESSAGE_H_ |
||||
|
||||
#include <stdbool.h> |
||||
|
||||
#include "def.h" |
||||
|
||||
// Registers the PHP Message class.
|
||||
void Message_ModuleInit(); |
||||
|
||||
// Gets a upb_msg* for the PHP object |val|, which must either be a Message
|
||||
// object or 'null'. Returns true and stores the message in |msg| if the
|
||||
// conversion succeeded (we can't return upb_msg* because null->NULL is a valid
|
||||
// conversion). Returns false and raises a PHP error if this isn't a Message
|
||||
// object or null, or if the Message object doesn't match this Descriptor.
|
||||
//
|
||||
// The given |arena| will be fused to this message's arena.
|
||||
bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena, |
||||
upb_msg **msg); |
||||
|
||||
// Gets or creates a PHP Message object to wrap the given upb_msg* and |desc|
|
||||
// and returns it in |val|. The PHP object will keep a reference to this |arena|
|
||||
// to ensure the underlying message data stays alive.
|
||||
//
|
||||
// If |msg| is NULL, this will return a PHP null.
|
||||
void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg, |
||||
zval *arena); |
||||
|
||||
#endif // PHP_PROTOBUF_MESSAGE_H_
|
@ -0,0 +1,226 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "names.h" |
||||
|
||||
#include <stdlib.h> |
||||
|
||||
#include "protobuf.h" |
||||
|
||||
/* stringsink *****************************************************************/ |
||||
|
||||
typedef struct { |
||||
char *ptr; |
||||
size_t len, size; |
||||
} stringsink; |
||||
|
||||
static size_t stringsink_string(stringsink *sink, const char *ptr, size_t len) { |
||||
size_t new_size = sink->size; |
||||
|
||||
while (sink->len + len > new_size) { |
||||
new_size *= 2; |
||||
} |
||||
|
||||
if (new_size != sink->size) { |
||||
sink->ptr = realloc(sink->ptr, new_size); |
||||
sink->size = new_size; |
||||
} |
||||
|
||||
memcpy(sink->ptr + sink->len, ptr, len); |
||||
sink->len += len; |
||||
|
||||
return len; |
||||
} |
||||
|
||||
static void stringsink_init(stringsink *sink) { |
||||
sink->size = 32; |
||||
sink->ptr = malloc(sink->size); |
||||
PBPHP_ASSERT(sink->ptr != NULL); |
||||
sink->len = 0; |
||||
} |
||||
|
||||
static void stringsink_uninit(stringsink *sink) { free(sink->ptr); } |
||||
|
||||
/* def name -> classname ******************************************************/ |
||||
|
||||
const char *const kReservedNames[] = { |
||||
"abstract", "and", "array", "as", "break", |
||||
"callable", "case", "catch", "class", "clone", |
||||
"const", "continue", "declare", "default", "die", |
||||
"do", "echo", "else", "elseif", "empty", |
||||
"enddeclare", "endfor", "endforeach", "endif", "endswitch", |
||||
"endwhile", "eval", "exit", "extends", "final", |
||||
"for", "foreach", "function", "global", "goto", |
||||
"if", "implements", "include", "include_once", "instanceof", |
||||
"insteadof", "interface", "isset", "list", "namespace", |
||||
"new", "or", "print", "private", "protected", |
||||
"public", "require", "require_once", "return", "static", |
||||
"switch", "throw", "trait", "try", "unset", |
||||
"use", "var", "while", "xor", "int", |
||||
"float", "bool", "string", "true", "false", |
||||
"null", "void", "iterable", NULL}; |
||||
|
||||
bool is_reserved_name(const char* name) { |
||||
int i; |
||||
for (i = 0; kReservedNames[i]; i++) { |
||||
if (strcmp(kReservedNames[i], name) == 0) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
static char nolocale_tolower(char ch) { |
||||
if (ch >= 'A' && ch <= 'Z') { |
||||
return ch - ('A' - 'a'); |
||||
} else { |
||||
return ch; |
||||
} |
||||
} |
||||
|
||||
static char nolocale_toupper(char ch) { |
||||
if (ch >= 'a' && ch <= 'z') { |
||||
return ch - ('a' - 'A'); |
||||
} else { |
||||
return ch; |
||||
} |
||||
} |
||||
|
||||
static bool is_reserved(const char *segment, int length) { |
||||
bool result; |
||||
char* lower = calloc(1, length + 1); |
||||
memcpy(lower, segment, length); |
||||
int i = 0; |
||||
while(lower[i]) { |
||||
lower[i] = nolocale_tolower(lower[i]); |
||||
i++; |
||||
} |
||||
lower[length] = 0; |
||||
result = is_reserved_name(lower); |
||||
free(lower); |
||||
return result; |
||||
} |
||||
|
||||
static void fill_prefix(const char *segment, int length, |
||||
const char *prefix_given, |
||||
const char *package_name, |
||||
stringsink *classname) { |
||||
if (prefix_given != NULL && strcmp(prefix_given, "") != 0) { |
||||
stringsink_string(classname, prefix_given, strlen(prefix_given)); |
||||
} else { |
||||
if (is_reserved(segment, length)) { |
||||
if (package_name != NULL && |
||||
strcmp("google.protobuf", package_name) == 0) { |
||||
stringsink_string(classname, "GPB", 3); |
||||
} else { |
||||
stringsink_string(classname, "PB", 2); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void fill_segment(const char *segment, int length, |
||||
stringsink *classname, bool use_camel) { |
||||
if (use_camel && (segment[0] < 'A' || segment[0] > 'Z')) { |
||||
char first = nolocale_toupper(segment[0]); |
||||
stringsink_string(classname, &first, 1); |
||||
stringsink_string(classname, segment + 1, length - 1); |
||||
} else { |
||||
stringsink_string(classname, segment, length); |
||||
} |
||||
} |
||||
|
||||
static void fill_namespace(const char *package, const char *php_namespace, |
||||
stringsink *classname) { |
||||
if (php_namespace != NULL) { |
||||
if (strlen(php_namespace) != 0) { |
||||
stringsink_string(classname, php_namespace, strlen(php_namespace)); |
||||
stringsink_string(classname, "\\", 1); |
||||
} |
||||
} else if (package != NULL) { |
||||
int i = 0, j = 0; |
||||
size_t package_len = strlen(package); |
||||
while (i < package_len) { |
||||
j = i; |
||||
while (j < package_len && package[j] != '.') { |
||||
j++; |
||||
} |
||||
fill_prefix(package + i, j - i, "", package, classname); |
||||
fill_segment(package + i, j - i, classname, true); |
||||
stringsink_string(classname, "\\", 1); |
||||
i = j + 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void fill_classname(const char *fullname, |
||||
const char *package, |
||||
const char *prefix, |
||||
stringsink *classname) { |
||||
int classname_start = 0; |
||||
if (package != NULL) { |
||||
size_t package_len = strlen(package); |
||||
classname_start = package_len == 0 ? 0 : package_len + 1; |
||||
} |
||||
size_t fullname_len = strlen(fullname); |
||||
|
||||
int i = classname_start, j; |
||||
while (i < fullname_len) { |
||||
j = i; |
||||
while (j < fullname_len && fullname[j] != '.') { |
||||
j++; |
||||
} |
||||
fill_prefix(fullname + i, j - i, prefix, package, classname); |
||||
fill_segment(fullname + i, j - i, classname, false); |
||||
if (j != fullname_len) { |
||||
stringsink_string(classname, "\\", 1); |
||||
} |
||||
i = j + 1; |
||||
} |
||||
} |
||||
|
||||
char *GetPhpClassname(const upb_filedef *file, const char *fullname) { |
||||
// Prepend '.' to package name to make it absolute. In the 5 additional
|
||||
// bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if
|
||||
// given message is google.protobuf.Empty.
|
||||
const char *package = upb_filedef_package(file); |
||||
const char *php_namespace = upb_filedef_phpnamespace(file); |
||||
const char *prefix = upb_filedef_phpprefix(file); |
||||
char *ret; |
||||
stringsink namesink; |
||||
stringsink_init(&namesink); |
||||
|
||||
fill_namespace(package, php_namespace, &namesink); |
||||
fill_classname(fullname, package, prefix, &namesink); |
||||
stringsink_string(&namesink, "\0", 1); |
||||
ret = strdup(namesink.ptr); |
||||
stringsink_uninit(&namesink); |
||||
return ret; |
||||
} |
@ -0,0 +1,40 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_NAMES_H_ |
||||
#define PHP_PROTOBUF_NAMES_H_ |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// Translates a protobuf symbol name (eg. foo.bar.Baz) into a PHP class name
|
||||
// (eg. \Foo\Bar\Baz).
|
||||
char *GetPhpClassname(const upb_filedef *file, const char *fullname); |
||||
|
||||
#endif // PHP_PROTOBUF_NAMES_H_
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,349 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "protobuf.h" |
||||
|
||||
#include <php.h> |
||||
#include <Zend/zend_interfaces.h> |
||||
|
||||
#include "arena.h" |
||||
#include "array.h" |
||||
#include "bundled_php.h" |
||||
#include "convert.h" |
||||
#include "def.h" |
||||
#include "map.h" |
||||
#include "message.h" |
||||
#include "names.h" |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module "globals"
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Despite the name, module "globals" are really thread-locals:
|
||||
// * PROTOBUF_G(var) accesses the thread-local variable for 'var'. Either:
|
||||
// * PROTOBUF_G(var) -> protobuf_globals.var (Non-ZTS / non-thread-safe)
|
||||
// * PROTOBUF_G(var) -> <Zend magic> (ZTS / thread-safe builds)
|
||||
|
||||
#define PROTOBUF_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(protobuf, v) |
||||
|
||||
ZEND_BEGIN_MODULE_GLOBALS(protobuf) |
||||
// Set by the user to make the descriptor pool persist between requests.
|
||||
zend_bool keep_descriptor_pool_after_request; |
||||
|
||||
// Currently we make the generated pool a "global", which means that if a user
|
||||
// does explicitly create threads within their request, the other threads will
|
||||
// get different results from DescriptorPool::getGeneratedPool(). We require
|
||||
// that all descriptors are loaded from the main thread.
|
||||
zval generated_pool; |
||||
|
||||
// A upb_symtab that we are saving for the next request so that we don't have
|
||||
// to rebuild it from scratch. When keep_descriptor_pool_after_request==true,
|
||||
// we steal the upb_symtab from the global DescriptorPool object just before
|
||||
// destroying it.
|
||||
upb_symtab *saved_symtab; |
||||
|
||||
// Object cache (see interface in protobuf.h).
|
||||
HashTable object_cache; |
||||
|
||||
// Name cache (see interface in protobuf.h).
|
||||
HashTable name_msg_cache; |
||||
HashTable name_enum_cache; |
||||
ZEND_END_MODULE_GLOBALS(protobuf) |
||||
|
||||
ZEND_DECLARE_MODULE_GLOBALS(protobuf) |
||||
|
||||
const zval *get_generated_pool() { |
||||
return &PROTOBUF_G(generated_pool); |
||||
} |
||||
|
||||
// This is a PHP extension (not a Zend extension). What follows is a summary of
|
||||
// a PHP extension's lifetime and when various handlers are called.
|
||||
//
|
||||
// * PHP_GINIT_FUNCTION(protobuf) / PHP_GSHUTDOWN_FUNCTION(protobuf)
|
||||
// are the constructor/destructor for the globals. The sequence over the
|
||||
// course of a process lifetime is:
|
||||
//
|
||||
// # Process startup
|
||||
// GINIT(<Main Thread Globals>)
|
||||
// MINIT
|
||||
//
|
||||
// foreach request:
|
||||
// RINIT
|
||||
// # Request is processed here.
|
||||
// RSHUTDOWN
|
||||
//
|
||||
// foreach thread:
|
||||
// GINIT(<This Thread Globals>)
|
||||
// # Code for the thread runs here.
|
||||
// GSHUTDOWN(<This Thread Globals>)
|
||||
//
|
||||
// # Process Shutdown
|
||||
// #
|
||||
// # These should be running per the docs, but I have not been able to
|
||||
// # actually get the process-wide shutdown functions to run.
|
||||
// #
|
||||
// # MSHUTDOWN
|
||||
// # GSHUTDOWN(<Main Thread Globals>)
|
||||
//
|
||||
// * Threads can be created either explicitly by the user, inside a request,
|
||||
// or implicitly by the runtime, to process multiple requests concurrently.
|
||||
// If the latter is being used, then the "foreach thread" block above
|
||||
// actually looks like this:
|
||||
//
|
||||
// foreach thread:
|
||||
// GINIT(<This Thread Globals>)
|
||||
// # A non-main thread will only receive requests when using a threaded
|
||||
// # MPM with Apache
|
||||
// foreach request:
|
||||
// RINIT
|
||||
// # Request is processed here.
|
||||
// RSHUTDOWN
|
||||
// GSHUTDOWN(<This Thread Globals>)
|
||||
//
|
||||
// That said, it appears that few people use threads with PHP:
|
||||
// * The pthread package documented at
|
||||
// https://www.php.net/manual/en/class.thread.php nas not been released
|
||||
// since 2016, and the current release fails to compile against any PHP
|
||||
// newer than 7.0.33.
|
||||
// * The GitHub master branch supports 7.2+, but this has not been released
|
||||
// to PECL.
|
||||
// * Its owner has disavowed it as "broken by design" and "in an untenable
|
||||
// position for the future": https://github.com/krakjoe/pthreads/issues/929
|
||||
// * The only way to use PHP with requests in different threads is to use the
|
||||
// Apache 2 mod_php with the "worker" MPM. But this is explicitly
|
||||
// discouraged by the documentation: https://serverfault.com/a/231660
|
||||
|
||||
static PHP_GSHUTDOWN_FUNCTION(protobuf) { |
||||
if (protobuf_globals->saved_symtab) { |
||||
upb_symtab_free(protobuf_globals->saved_symtab); |
||||
} |
||||
} |
||||
|
||||
static PHP_GINIT_FUNCTION(protobuf) { |
||||
ZVAL_NULL(&protobuf_globals->generated_pool); |
||||
protobuf_globals->saved_symtab = NULL; |
||||
} |
||||
|
||||
/**
|
||||
* PHP_RINIT_FUNCTION(protobuf) |
||||
* |
||||
* This function is run at the beginning of processing each request. |
||||
*/ |
||||
static PHP_RINIT_FUNCTION(protobuf) { |
||||
// Create the global generated pool.
|
||||
// Reuse the symtab (if any) left to us by the last request.
|
||||
upb_symtab *symtab = PROTOBUF_G(saved_symtab); |
||||
DescriptorPool_CreateWithSymbolTable(&PROTOBUF_G(generated_pool), symtab); |
||||
|
||||
// Set up autoloader for bundled sources.
|
||||
zend_eval_string("spl_autoload_register('protobuf_internal_loadbundled');", |
||||
NULL, "autoload_register.php"); |
||||
|
||||
zend_hash_init(&PROTOBUF_G(object_cache), 64, NULL, NULL, 0); |
||||
zend_hash_init(&PROTOBUF_G(name_msg_cache), 64, NULL, NULL, 0); |
||||
zend_hash_init(&PROTOBUF_G(name_enum_cache), 64, NULL, NULL, 0); |
||||
|
||||
return SUCCESS; |
||||
} |
||||
|
||||
/**
|
||||
* PHP_RSHUTDOWN_FUNCTION(protobuf) |
||||
* |
||||
* This function is run at the end of processing each request. |
||||
*/ |
||||
static PHP_RSHUTDOWN_FUNCTION(protobuf) { |
||||
// Preserve the symtab if requested.
|
||||
if (PROTOBUF_G(keep_descriptor_pool_after_request)) { |
||||
zval *zv = &PROTOBUF_G(generated_pool); |
||||
PROTOBUF_G(saved_symtab) = DescriptorPool_Steal(zv); |
||||
} |
||||
|
||||
zval_dtor(&PROTOBUF_G(generated_pool)); |
||||
zend_hash_destroy(&PROTOBUF_G(object_cache)); |
||||
zend_hash_destroy(&PROTOBUF_G(name_msg_cache)); |
||||
zend_hash_destroy(&PROTOBUF_G(name_enum_cache)); |
||||
|
||||
return SUCCESS; |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Bundled PHP sources
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// We bundle PHP sources for well-known types into the C extension. There is no
|
||||
// need to implement these in C.
|
||||
|
||||
static PHP_FUNCTION(protobuf_internal_loadbundled) { |
||||
char *name = NULL; |
||||
zend_long size; |
||||
BundledPhp_File *file; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &size) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
for (file = bundled_files; file->filename; file++) { |
||||
if (strcmp(file->filename, name) == 0) { |
||||
zend_eval_string((char*)file->contents, NULL, (char*)file->filename); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_load_bundled_source, 0, 0, 1) |
||||
ZEND_ARG_INFO(0, class_name) |
||||
ZEND_END_ARG_INFO() |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Object Cache.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ObjCache_Add(const void *upb_obj, zend_object *php_obj) { |
||||
zend_ulong k = (zend_ulong)upb_obj; |
||||
zend_hash_index_add_ptr(&PROTOBUF_G(object_cache), k, php_obj); |
||||
} |
||||
|
||||
void ObjCache_Delete(const void *upb_obj) { |
||||
if (upb_obj) { |
||||
zend_ulong k = (zend_ulong)upb_obj; |
||||
int ret = zend_hash_index_del(&PROTOBUF_G(object_cache), k); |
||||
PBPHP_ASSERT(ret == SUCCESS); |
||||
} |
||||
} |
||||
|
||||
bool ObjCache_Get(const void *upb_obj, zval *val) { |
||||
zend_ulong k = (zend_ulong)upb_obj; |
||||
zend_object *obj = zend_hash_index_find_ptr(&PROTOBUF_G(object_cache), k); |
||||
|
||||
if (obj) { |
||||
GC_ADDREF(obj); |
||||
ZVAL_OBJ(val, obj); |
||||
return true; |
||||
} else { |
||||
ZVAL_NULL(val); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Name Cache.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void NameMap_AddMessage(const upb_msgdef *m) { |
||||
char *k = GetPhpClassname(upb_msgdef_file(m), upb_msgdef_fullname(m)); |
||||
zend_hash_str_add_ptr(&PROTOBUF_G(name_msg_cache), k, strlen(k), (void*)m); |
||||
free(k); |
||||
} |
||||
|
||||
void NameMap_AddEnum(const upb_enumdef *e) { |
||||
char *k = GetPhpClassname(upb_enumdef_file(e), upb_enumdef_fullname(e)); |
||||
zend_hash_str_add_ptr(&PROTOBUF_G(name_enum_cache), k, strlen(k), (void*)e); |
||||
free(k); |
||||
} |
||||
|
||||
const upb_msgdef *NameMap_GetMessage(zend_class_entry *ce) { |
||||
const upb_msgdef *ret = |
||||
zend_hash_find_ptr(&PROTOBUF_G(name_msg_cache), ce->name); |
||||
|
||||
if (!ret && ce->create_object) { |
||||
zval tmp; |
||||
zval zv; |
||||
ZVAL_OBJ(&tmp, ce->create_object(ce)); |
||||
zend_call_method_with_0_params(&tmp, ce, NULL, "__construct", &zv); |
||||
zval_ptr_dtor(&tmp); |
||||
zval_ptr_dtor(&zv); |
||||
ret = zend_hash_find_ptr(&PROTOBUF_G(name_msg_cache), ce->name); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
const upb_enumdef *NameMap_GetEnum(zend_class_entry *ce) { |
||||
const upb_enumdef *ret = |
||||
zend_hash_find_ptr(&PROTOBUF_G(name_enum_cache), ce->name); |
||||
return ret; |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module init.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
zend_function_entry protobuf_functions[] = { |
||||
PHP_FE(protobuf_internal_loadbundled, arginfo_load_bundled_source) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
static const zend_module_dep protobuf_deps[] = { |
||||
ZEND_MOD_OPTIONAL("date") |
||||
ZEND_MOD_END |
||||
}; |
||||
|
||||
PHP_INI_BEGIN() |
||||
STD_PHP_INI_ENTRY("protobuf.keep_descriptor_pool_after_request", "0", |
||||
PHP_INI_SYSTEM, OnUpdateBool, |
||||
keep_descriptor_pool_after_request, zend_protobuf_globals, |
||||
protobuf_globals) |
||||
PHP_INI_END() |
||||
|
||||
static PHP_MINIT_FUNCTION(protobuf) { |
||||
REGISTER_INI_ENTRIES(); |
||||
Arena_ModuleInit(); |
||||
Array_ModuleInit(); |
||||
Convert_ModuleInit(); |
||||
Def_ModuleInit(); |
||||
Map_ModuleInit(); |
||||
Message_ModuleInit(); |
||||
return SUCCESS; |
||||
} |
||||
|
||||
static PHP_MSHUTDOWN_FUNCTION(protobuf) { |
||||
return SUCCESS; |
||||
} |
||||
|
||||
zend_module_entry protobuf_module_entry = { |
||||
STANDARD_MODULE_HEADER_EX, |
||||
NULL, |
||||
protobuf_deps, |
||||
"protobuf", // extension name
|
||||
protobuf_functions, // function list
|
||||
PHP_MINIT(protobuf), // process startup
|
||||
PHP_MSHUTDOWN(protobuf), // process shutdown
|
||||
PHP_RINIT(protobuf), // request shutdown
|
||||
PHP_RSHUTDOWN(protobuf), // request shutdown
|
||||
NULL, // extension info
|
||||
"3.13.0", // extension version
|
||||
PHP_MODULE_GLOBALS(protobuf), // globals descriptor
|
||||
PHP_GINIT(protobuf), // globals ctor
|
||||
PHP_GSHUTDOWN(protobuf), // globals dtor
|
||||
NULL, // post deactivate
|
||||
STANDARD_MODULE_PROPERTIES_EX |
||||
}; |
||||
|
||||
ZEND_GET_MODULE(protobuf) |
@ -0,0 +1,89 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef PHP_PROTOBUF_H_ |
||||
#define PHP_PROTOBUF_H_ |
||||
|
||||
#include <php.h> |
||||
#include <stdbool.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
const zval *get_generated_pool(); |
||||
|
||||
#if PHP_VERSION_ID < 70300 |
||||
#define GC_ADDREF(h) ++GC_REFCOUNT(h) |
||||
#define GC_DELREF(h) --GC_REFCOUNT(h) |
||||
#endif |
||||
|
||||
// ptr -> PHP object cache. This is a weak map that caches lazily-created
|
||||
// wrapper objects around upb types:
|
||||
// * upb_msg* -> Message
|
||||
// * upb_array* -> RepeatedField
|
||||
// * upb_map*, -> MapField
|
||||
// * upb_msgdef* -> Descriptor
|
||||
// * upb_enumdef* -> EnumDescriptor
|
||||
// * zend_class_entry* -> Descriptor
|
||||
//
|
||||
// Each wrapped object should add itself to the map when it is constructed, and
|
||||
// remove itself from the map when it is destroyed. This is how we ensure that
|
||||
// the map only contains live objects. The map is weak so it does not actually
|
||||
// take references to the cached objects.
|
||||
void ObjCache_Add(const void *key, zend_object *php_obj); |
||||
void ObjCache_Delete(const void *key); |
||||
bool ObjCache_Get(const void *key, zval *val); |
||||
|
||||
// PHP class name map. This is necessary because the pb_name->php_class_name
|
||||
// transformation is non-reversible, so when we need to look up a msgdef or
|
||||
// enumdef by PHP class, we can't turn the class name into a pb_name.
|
||||
// * php_class_name -> upb_msgdef*
|
||||
// * php_class_name -> upb_enumdef*
|
||||
void NameMap_AddMessage(const upb_msgdef *m); |
||||
void NameMap_AddEnum(const upb_enumdef *m); |
||||
const upb_msgdef *NameMap_GetMessage(zend_class_entry *ce); |
||||
const upb_enumdef *NameMap_GetEnum(zend_class_entry *ce); |
||||
|
||||
// We need our own assert() because PHP takes control of NDEBUG in its headers.
|
||||
#ifdef PBPHP_ENABLE_ASSERTS |
||||
#define PBPHP_ASSERT(x) \ |
||||
do { \
|
||||
if (!(x)) { \
|
||||
fprintf(stderr, "Assertion failure at %s:%d %s", __FILE__, __LINE__, \
|
||||
#x); \ |
||||
abort(); \
|
||||
} \
|
||||
} while (false) |
||||
#else |
||||
#define PBPHP_ASSERT(x) \ |
||||
do { \
|
||||
} while (false && (x)) |
||||
#endif |
||||
|
||||
#endif // PHP_PROTOBUF_H_
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue