dep-protobuf/objectivec/GPBExtensionInternals.m
Thomas Van Lenten d260bff8c0 [ObjC] Remove the need to tracking MessageSet on extension field descriptors
The flag is still set, but the runtime doesn't need to check it, instead it uses
the bit set on the message. It also streamlines some of the serialization since
we aren't checking the bit at some of the lower levels.

PiperOrigin-RevId: 725208757
2025-02-10 07:50:22 -08:00

250 lines
8.8 KiB
Objective-C

// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#import "GPBExtensionInternals.h"
#import <objc/runtime.h>
#import "GPBCodedInputStream.h"
#import "GPBCodedInputStream_PackagePrivate.h"
#import "GPBCodedOutputStream.h"
#import "GPBCodedOutputStream_PackagePrivate.h"
#import "GPBDescriptor.h"
#import "GPBDescriptor_PackagePrivate.h"
#import "GPBMessage.h"
#import "GPBMessage_PackagePrivate.h"
#import "GPBUtilities.h"
#import "GPBUtilities_PackagePrivate.h"
GPB_INLINE size_t DataTypeSize(GPBDataType dataType) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch-enum"
switch (dataType) {
case GPBDataTypeBool:
return 1;
case GPBDataTypeFixed32:
case GPBDataTypeSFixed32:
case GPBDataTypeFloat:
return 4;
case GPBDataTypeFixed64:
case GPBDataTypeSFixed64:
case GPBDataTypeDouble:
return 8;
default:
return 0;
}
#pragma clang diagnostic pop
}
static size_t ComputePBSerializedSizeNoTagOfObject(GPBDataType dataType, id object) {
#define FIELD_CASE(TYPE, ACCESSOR) \
case GPBDataType##TYPE: \
return GPBCompute##TYPE##SizeNoTag([(NSNumber *)object ACCESSOR]);
#define FIELD_CASE2(TYPE) \
case GPBDataType##TYPE: \
return GPBCompute##TYPE##SizeNoTag(object);
switch (dataType) {
FIELD_CASE(Bool, boolValue)
FIELD_CASE(Float, floatValue)
FIELD_CASE(Double, doubleValue)
FIELD_CASE(Int32, intValue)
FIELD_CASE(SFixed32, intValue)
FIELD_CASE(SInt32, intValue)
FIELD_CASE(Enum, intValue)
FIELD_CASE(Int64, longLongValue)
FIELD_CASE(SInt64, longLongValue)
FIELD_CASE(SFixed64, longLongValue)
FIELD_CASE(UInt32, unsignedIntValue)
FIELD_CASE(Fixed32, unsignedIntValue)
FIELD_CASE(UInt64, unsignedLongLongValue)
FIELD_CASE(Fixed64, unsignedLongLongValue)
FIELD_CASE2(Bytes)
FIELD_CASE2(String)
FIELD_CASE2(Message)
FIELD_CASE2(Group)
}
#undef FIELD_CASE
#undef FIELD_CASE2
}
static size_t ComputeSerializedSizeIncludingTagOfObject(GPBExtensionDescription *description,
id object) {
#define FIELD_CASE(TYPE, ACCESSOR) \
case GPBDataType##TYPE: \
return GPBCompute##TYPE##Size(description->fieldNumber, [(NSNumber *)object ACCESSOR]);
#define FIELD_CASE2(TYPE) \
case GPBDataType##TYPE: \
return GPBCompute##TYPE##Size(description->fieldNumber, object);
switch (description->dataType) {
FIELD_CASE(Bool, boolValue)
FIELD_CASE(Float, floatValue)
FIELD_CASE(Double, doubleValue)
FIELD_CASE(Int32, intValue)
FIELD_CASE(SFixed32, intValue)
FIELD_CASE(SInt32, intValue)
FIELD_CASE(Enum, intValue)
FIELD_CASE(Int64, longLongValue)
FIELD_CASE(SInt64, longLongValue)
FIELD_CASE(SFixed64, longLongValue)
FIELD_CASE(UInt32, unsignedIntValue)
FIELD_CASE(Fixed32, unsignedIntValue)
FIELD_CASE(UInt64, unsignedLongLongValue)
FIELD_CASE(Fixed64, unsignedLongLongValue)
FIELD_CASE2(Bytes)
FIELD_CASE2(String)
FIELD_CASE2(Group)
FIELD_CASE2(Message)
}
#undef FIELD_CASE
#undef FIELD_CASE2
}
static size_t ComputeSerializedSizeIncludingTagOfArray(GPBExtensionDescription *description,
NSArray *values) {
if (GPBExtensionIsPacked(description)) {
size_t size = 0;
size_t typeSize = DataTypeSize(description->dataType);
if (typeSize != 0) {
size = values.count * typeSize;
} else {
for (id value in values) {
size += ComputePBSerializedSizeNoTagOfObject(description->dataType, value);
}
}
return size + GPBComputeTagSize(description->fieldNumber) +
GPBComputeRawVarint32SizeForInteger(size);
} else {
size_t size = 0;
for (id value in values) {
size += ComputeSerializedSizeIncludingTagOfObject(description, value);
}
return size;
}
}
static void WriteObjectIncludingTagToCodedOutputStream(id object,
GPBExtensionDescription *description,
GPBCodedOutputStream *output) {
#define FIELD_CASE(TYPE, ACCESSOR) \
case GPBDataType##TYPE: \
[output write##TYPE:description->fieldNumber value:[(NSNumber *)object ACCESSOR]]; \
return;
#define FIELD_CASE2(TYPE) \
case GPBDataType##TYPE: \
[output write##TYPE:description->fieldNumber value:object]; \
return;
switch (description->dataType) {
FIELD_CASE(Bool, boolValue)
FIELD_CASE(Float, floatValue)
FIELD_CASE(Double, doubleValue)
FIELD_CASE(Int32, intValue)
FIELD_CASE(SFixed32, intValue)
FIELD_CASE(SInt32, intValue)
FIELD_CASE(Enum, intValue)
FIELD_CASE(Int64, longLongValue)
FIELD_CASE(SInt64, longLongValue)
FIELD_CASE(SFixed64, longLongValue)
FIELD_CASE(UInt32, unsignedIntValue)
FIELD_CASE(Fixed32, unsignedIntValue)
FIELD_CASE(UInt64, unsignedLongLongValue)
FIELD_CASE(Fixed64, unsignedLongLongValue)
FIELD_CASE2(Bytes)
FIELD_CASE2(String)
FIELD_CASE2(Group)
FIELD_CASE2(Message)
}
#undef FIELD_CASE
#undef FIELD_CASE2
}
static void WriteObjectNoTagToCodedOutputStream(id object, GPBExtensionDescription *description,
GPBCodedOutputStream *output) {
#define FIELD_CASE(TYPE, ACCESSOR) \
case GPBDataType##TYPE: \
[output write##TYPE##NoTag:[(NSNumber *)object ACCESSOR]]; \
return;
#define FIELD_CASE2(TYPE) \
case GPBDataType##TYPE: \
[output write##TYPE##NoTag:object]; \
return;
switch (description->dataType) {
FIELD_CASE(Bool, boolValue)
FIELD_CASE(Float, floatValue)
FIELD_CASE(Double, doubleValue)
FIELD_CASE(Int32, intValue)
FIELD_CASE(SFixed32, intValue)
FIELD_CASE(SInt32, intValue)
FIELD_CASE(Enum, intValue)
FIELD_CASE(Int64, longLongValue)
FIELD_CASE(SInt64, longLongValue)
FIELD_CASE(SFixed64, longLongValue)
FIELD_CASE(UInt32, unsignedIntValue)
FIELD_CASE(Fixed32, unsignedIntValue)
FIELD_CASE(UInt64, unsignedLongLongValue)
FIELD_CASE(Fixed64, unsignedLongLongValue)
FIELD_CASE2(Bytes)
FIELD_CASE2(String)
FIELD_CASE2(Message)
case GPBDataTypeGroup:
[output writeGroupNoTag:description->fieldNumber value:object];
return;
}
#undef FIELD_CASE
#undef FIELD_CASE2
}
static void WriteArrayIncludingTagsToCodedOutputStream(NSArray *values,
GPBExtensionDescription *description,
GPBCodedOutputStream *output) {
if (GPBExtensionIsPacked(description)) {
[output writeTag:description->fieldNumber format:GPBWireFormatLengthDelimited];
size_t dataSize = 0;
size_t typeSize = DataTypeSize(description->dataType);
if (typeSize != 0) {
dataSize = values.count * typeSize;
} else {
for (id value in values) {
dataSize += ComputePBSerializedSizeNoTagOfObject(description->dataType, value);
}
}
[output writeRawVarintSizeTAs32:dataSize];
for (id value in values) {
WriteObjectNoTagToCodedOutputStream(value, description, output);
}
} else {
for (id value in values) {
WriteObjectIncludingTagToCodedOutputStream(value, description, output);
}
}
}
// Direct access is use for speed, to avoid even internally declaring things
// read/write, etc. The warning is enabled in the project to ensure code calling
// protos can turn on -Wdirect-ivar-access without issues.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdirect-ivar-access"
void GPBWriteExtensionValueToOutputStream(GPBExtensionDescriptor *extension, id value,
GPBCodedOutputStream *output) {
GPBExtensionDescription *description = extension->description_;
if (GPBExtensionIsRepeated(description)) {
WriteArrayIncludingTagsToCodedOutputStream(value, description, output);
} else {
WriteObjectIncludingTagToCodedOutputStream(value, description, output);
}
}
size_t GPBComputeExtensionSerializedSizeIncludingTag(GPBExtensionDescriptor *extension, id value) {
GPBExtensionDescription *description = extension->description_;
if (GPBExtensionIsRepeated(description)) {
return ComputeSerializedSizeIncludingTagOfArray(description, value);
} else {
return ComputeSerializedSizeIncludingTagOfObject(description, value);
}
}
#pragma clang diagnostic pop