mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
975 lines
27 KiB
Objective-C
975 lines
27 KiB
Objective-C
// Copyright 2008 Cyrus Najmabadi
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#import "CodedOutputStream.h"
|
|
|
|
#import "Message.h"
|
|
#import "Utilities.h"
|
|
#import "WireFormat.h"
|
|
#import "UnknownFieldSet.h"
|
|
|
|
@interface PBCodedOutputStream ()
|
|
@property (retain) NSMutableData* buffer;
|
|
@property int32_t position;
|
|
@property (retain) NSOutputStream* output;
|
|
@end
|
|
|
|
|
|
@implementation PBCodedOutputStream
|
|
|
|
const int32_t DEFAULT_BUFFER_SIZE = 4096;
|
|
const int32_t LITTLE_ENDIAN_32_SIZE = 4;
|
|
const int32_t LITTLE_ENDIAN_64_SIZE = 8;
|
|
|
|
@synthesize output;
|
|
@synthesize buffer;
|
|
@synthesize position;
|
|
|
|
- (void) dealloc {
|
|
[output close];
|
|
self.output = nil;
|
|
self.buffer = nil;
|
|
self.position = 0;
|
|
}
|
|
|
|
|
|
- (id) initWithOutputStream:(NSOutputStream*) output_
|
|
data:(NSMutableData*) data_ {
|
|
if ((self = [super init])) {
|
|
self.output = output_;
|
|
self.buffer = data_;
|
|
self.position = 0;
|
|
|
|
[output open];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
|
|
+ (PBCodedOutputStream*) streamWithOutputStream:(NSOutputStream*) output
|
|
bufferSize:(int32_t) bufferSize {
|
|
NSMutableData* data = [NSMutableData dataWithLength:(NSUInteger)bufferSize];
|
|
return [[PBCodedOutputStream alloc] initWithOutputStream:output
|
|
data:data];
|
|
}
|
|
|
|
|
|
+ (PBCodedOutputStream*) streamWithOutputStream:(NSOutputStream*) output {
|
|
return [PBCodedOutputStream streamWithOutputStream:output bufferSize:DEFAULT_BUFFER_SIZE];
|
|
}
|
|
|
|
|
|
+ (PBCodedOutputStream*) streamWithData:(NSMutableData*) data {
|
|
return [[PBCodedOutputStream alloc] initWithOutputStream:nil data:data];
|
|
}
|
|
|
|
|
|
- (void) writeDoubleNoTag:(Float64) value {
|
|
[self writeRawLittleEndian64:convertFloat64ToInt64(value)];
|
|
}
|
|
|
|
|
|
/** Write a {@code double} field, including tag, to the stream. */
|
|
- (void) writeDouble:(int32_t) fieldNumber
|
|
value:(Float64) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatFixed64];
|
|
[self writeDoubleNoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeFloatNoTag:(Float32) value {
|
|
[self writeRawLittleEndian32:convertFloat32ToInt32(value)];
|
|
}
|
|
|
|
|
|
/** Write a {@code float} field, including tag, to the stream. */
|
|
- (void) writeFloat:(int32_t) fieldNumber
|
|
value:(Float32) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatFixed32];
|
|
[self writeFloatNoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeUInt64NoTag:(int64_t) value {
|
|
[self writeRawVarint64:value];
|
|
}
|
|
|
|
|
|
/** Write a {@code uint64} field, including tag, to the stream. */
|
|
- (void) writeUInt64:(int32_t) fieldNumber
|
|
value:(int64_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatVarint];
|
|
[self writeUInt64NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeInt64NoTag:(int64_t) value {
|
|
[self writeRawVarint64:value];
|
|
}
|
|
|
|
|
|
/** Write an {@code int64} field, including tag, to the stream. */
|
|
- (void) writeInt64:(int32_t) fieldNumber
|
|
value:(int64_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatVarint];
|
|
[self writeInt64NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeInt32NoTag:(int32_t) value {
|
|
if (value >= 0) {
|
|
[self writeRawVarint32:value];
|
|
} else {
|
|
// Must sign-extend
|
|
[self writeRawVarint64:value];
|
|
}
|
|
}
|
|
|
|
|
|
/** Write an {@code int32} field, including tag, to the stream. */
|
|
- (void) writeInt32:(int32_t) fieldNumber
|
|
value:(int32_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatVarint];
|
|
[self writeInt32NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeFixed64NoTag:(int64_t) value {
|
|
[self writeRawLittleEndian64:value];
|
|
}
|
|
|
|
|
|
/** Write a {@code fixed64} field, including tag, to the stream. */
|
|
- (void) writeFixed64:(int32_t) fieldNumber
|
|
value:(int64_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatFixed64];
|
|
[self writeFixed64NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeFixed32NoTag:(int32_t) value {
|
|
[self writeRawLittleEndian32:value];
|
|
}
|
|
|
|
|
|
/** Write a {@code fixed32} field, including tag, to the stream. */
|
|
- (void) writeFixed32:(int32_t) fieldNumber
|
|
value:(int32_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatFixed32];
|
|
[self writeFixed32NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeBoolNoTag:(BOOL) value {
|
|
[self writeRawByte:(value ? 1 : 0)];
|
|
}
|
|
|
|
|
|
/** Write a {@code bool} field, including tag, to the stream. */
|
|
- (void) writeBool:(int32_t) fieldNumber
|
|
value:(BOOL) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatVarint];
|
|
[self writeBoolNoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeStringNoTag:(NSString*) value {
|
|
NSData* data = [value dataUsingEncoding:NSUTF8StringEncoding];
|
|
[self writeRawVarint32:(int32_t)data.length];
|
|
[self writeRawData:data];
|
|
}
|
|
|
|
|
|
/** Write a {@code string} field, including tag, to the stream. */
|
|
- (void) writeString:(int32_t) fieldNumber
|
|
value:(NSString*) value {
|
|
// TODO(cyrusn): we could probably use:
|
|
// NSString:getBytes:maxLength:usedLength:encoding:options:range:remainingRange:
|
|
// to write directly into our buffer.
|
|
[self writeTag:fieldNumber format:PBWireFormatLengthDelimited];
|
|
[self writeStringNoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeGroupNoTag:(int32_t) fieldNumber
|
|
value:(id<PBMessage>) value {
|
|
[value writeToCodedOutputStream:self];
|
|
[self writeTag:fieldNumber format:PBWireFormatEndGroup];
|
|
}
|
|
|
|
|
|
/** Write a {@code group} field, including tag, to the stream. */
|
|
- (void) writeGroup:(int32_t) fieldNumber
|
|
value:(id<PBMessage>) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatStartGroup];
|
|
[self writeGroupNoTag:fieldNumber value:value];
|
|
}
|
|
|
|
|
|
- (void) writeUnknownGroupNoTag:(int32_t) fieldNumber
|
|
value:(PBUnknownFieldSet*) value {
|
|
[value writeToCodedOutputStream:self];
|
|
[self writeTag:fieldNumber format:PBWireFormatEndGroup];
|
|
}
|
|
|
|
|
|
/** Write a group represented by an {@link PBUnknownFieldSet}. */
|
|
- (void) writeUnknownGroup:(int32_t) fieldNumber
|
|
value:(PBUnknownFieldSet*) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatStartGroup];
|
|
[self writeUnknownGroupNoTag:fieldNumber value:value];
|
|
}
|
|
|
|
|
|
- (void) writeMessageNoTag:(id<PBMessage>) value {
|
|
[self writeRawVarint32:[value serializedSize]];
|
|
[value writeToCodedOutputStream:self];
|
|
}
|
|
|
|
|
|
/** Write an embedded message field, including tag, to the stream. */
|
|
- (void) writeMessage:(int32_t) fieldNumber
|
|
value:(id<PBMessage>) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatLengthDelimited];
|
|
[self writeMessageNoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeDataNoTag:(NSData*) value {
|
|
[self writeRawVarint32:(int32_t)value.length];
|
|
[self writeRawData:value];
|
|
}
|
|
|
|
|
|
/** Write a {@code bytes} field, including tag, to the stream. */
|
|
- (void) writeData:(int32_t) fieldNumber value:(NSData*) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatLengthDelimited];
|
|
[self writeDataNoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeUInt32NoTag:(int32_t) value {
|
|
[self writeRawVarint32:value];
|
|
}
|
|
|
|
|
|
/** Write a {@code uint32} field, including tag, to the stream. */
|
|
- (void) writeUInt32:(int32_t) fieldNumber
|
|
value:(int32_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatVarint];
|
|
[self writeUInt32NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeEnumNoTag:(int32_t) value {
|
|
[self writeRawVarint32:value];
|
|
}
|
|
|
|
|
|
/**
|
|
* Write an enum field, including tag, to the stream. Caller is responsible
|
|
* for converting the enum value to its numeric value.
|
|
*/
|
|
- (void) writeEnum:(int32_t) fieldNumber
|
|
value:(int32_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatVarint];
|
|
[self writeEnumNoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeSFixed32NoTag:(int32_t) value {
|
|
[self writeRawLittleEndian32:value];
|
|
}
|
|
|
|
|
|
/** Write an {@code sfixed32} field, including tag, to the stream. */
|
|
- (void) writeSFixed32:(int32_t) fieldNumber
|
|
value:(int32_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatFixed32];
|
|
[self writeSFixed32NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeSFixed64NoTag:(int64_t) value {
|
|
[self writeRawLittleEndian64:value];
|
|
}
|
|
|
|
|
|
/** Write an {@code sfixed64} field, including tag, to the stream. */
|
|
- (void) writeSFixed64:(int32_t) fieldNumber
|
|
value:(int64_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatFixed64];
|
|
[self writeSFixed64NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeSInt32NoTag:(int32_t) value {
|
|
[self writeRawVarint32:encodeZigZag32(value)];
|
|
}
|
|
|
|
|
|
/** Write an {@code sint32} field, including tag, to the stream. */
|
|
- (void) writeSInt32:(int32_t) fieldNumber
|
|
value:(int32_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatVarint];
|
|
[self writeSInt32NoTag:value];
|
|
}
|
|
|
|
|
|
- (void) writeSInt64NoTag:(int64_t) value {
|
|
[self writeRawVarint64:encodeZigZag64(value)];
|
|
}
|
|
|
|
|
|
/** Write an {@code sint64} field, including tag, to the stream. */
|
|
- (void) writeSInt64:(int32_t) fieldNumber
|
|
value:(int64_t) value {
|
|
[self writeTag:fieldNumber format:PBWireFormatVarint];
|
|
[self writeSInt64NoTag:value];
|
|
}
|
|
|
|
|
|
/**
|
|
* Write a MessageSet extension field to the stream. For historical reasons,
|
|
* the wire format differs from normal fields.
|
|
*/
|
|
- (void) writeMessageSetExtension:(int32_t) fieldNumber
|
|
value:(id<PBMessage>) value {
|
|
[self writeTag:PBWireFormatMessageSetItem format:PBWireFormatStartGroup];
|
|
[self writeUInt32:PBWireFormatMessageSetTypeId value:fieldNumber];
|
|
[self writeMessage:PBWireFormatMessageSetMessage value:value];
|
|
[self writeTag:PBWireFormatMessageSetItem format:PBWireFormatEndGroup];
|
|
}
|
|
|
|
|
|
/**
|
|
* Write an unparsed MessageSet extension field to the stream. For
|
|
* historical reasons, the wire format differs from normal fields.
|
|
*/
|
|
- (void) writeRawMessageSetExtension:(int32_t) fieldNumber
|
|
value:(NSData*) value {
|
|
[self writeTag:PBWireFormatMessageSetItem format:PBWireFormatStartGroup];
|
|
[self writeUInt32:PBWireFormatMessageSetTypeId value:fieldNumber];
|
|
[self writeData:PBWireFormatMessageSetMessage value:value];
|
|
[self writeTag:PBWireFormatMessageSetItem format:PBWireFormatEndGroup];
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code double} field, including tag.
|
|
*/
|
|
int32_t computeDoubleSizeNoTag(Float64 value) {
|
|
return LITTLE_ENDIAN_64_SIZE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code float} field, including tag.
|
|
*/
|
|
int32_t computeFloatSizeNoTag(Float32 value) {
|
|
return LITTLE_ENDIAN_32_SIZE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code uint64} field, including tag.
|
|
*/
|
|
int32_t computeUInt64SizeNoTag(int64_t value) {
|
|
return computeRawVarint64Size(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code int64} field, including tag.
|
|
*/
|
|
int32_t computeInt64SizeNoTag(int64_t value) {
|
|
return computeRawVarint64Size(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code int32} field, including tag.
|
|
*/
|
|
int32_t computeInt32SizeNoTag(int32_t value) {
|
|
if (value >= 0) {
|
|
return computeRawVarint32Size(value);
|
|
} else {
|
|
// Must sign-extend.
|
|
return 10;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code fixed64} field, including tag.
|
|
*/
|
|
int32_t computeFixed64SizeNoTag(int64_t value) {
|
|
return LITTLE_ENDIAN_64_SIZE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code fixed32} field, including tag.
|
|
*/
|
|
int32_t computeFixed32SizeNoTag(int32_t value) {
|
|
return LITTLE_ENDIAN_32_SIZE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code bool} field, including tag.
|
|
*/
|
|
int32_t computeBoolSizeNoTag(BOOL value) {
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code string} field, including tag.
|
|
*/
|
|
int32_t computeStringSizeNoTag(NSString* value) {
|
|
NSData* data = [value dataUsingEncoding:NSUTF8StringEncoding];
|
|
return computeRawVarint32Size((int32_t)data.length) + (int32_t)data.length;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code group} field, including tag.
|
|
*/
|
|
int32_t computeGroupSizeNoTag(id<PBMessage> value) {
|
|
return [value serializedSize];
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code group} field represented by an {@code PBUnknownFieldSet}, including
|
|
* tag.
|
|
*/
|
|
int32_t computeUnknownGroupSizeNoTag(PBUnknownFieldSet* value) {
|
|
return value.serializedSize;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* embedded message field, including tag.
|
|
*/
|
|
int32_t computeMessageSizeNoTag(id<PBMessage> value) {
|
|
int32_t size = [value serializedSize];
|
|
return computeRawVarint32Size(size) + size;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code bytes} field, including tag.
|
|
*/
|
|
int32_t computeDataSizeNoTag(NSData* value) {
|
|
return computeRawVarint32Size((int32_t)value.length) + (int32_t)value.length;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code uint32} field, including tag.
|
|
*/
|
|
int32_t computeUInt32SizeNoTag(int32_t value) {
|
|
return computeRawVarint32Size(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* enum field, including tag. Caller is responsible for converting the
|
|
* enum value to its numeric value.
|
|
*/
|
|
int32_t computeEnumSizeNoTag(int32_t value) {
|
|
return computeRawVarint32Size(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code sfixed32} field, including tag.
|
|
*/
|
|
int32_t computeSFixed32SizeNoTag(int32_t value) {
|
|
return LITTLE_ENDIAN_32_SIZE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code sfixed64} field, including tag.
|
|
*/
|
|
int32_t computeSFixed64SizeNoTag(int64_t value) {
|
|
return LITTLE_ENDIAN_64_SIZE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code sint32} field, including tag.
|
|
*/
|
|
int32_t computeSInt32SizeNoTag(int32_t value) {
|
|
return computeRawVarint32Size(encodeZigZag32(value));
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code sint64} field, including tag.
|
|
*/
|
|
int32_t computeSInt64SizeNoTag(int64_t value) {
|
|
return computeRawVarint64Size(encodeZigZag64(value));
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code double} field, including tag.
|
|
*/
|
|
int32_t computeDoubleSize(int32_t fieldNumber, Float64 value) {
|
|
return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code float} field, including tag.
|
|
*/
|
|
int32_t computeFloatSize(int32_t fieldNumber, Float32 value) {
|
|
return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code uint64} field, including tag.
|
|
*/
|
|
int32_t computeUInt64Size(int32_t fieldNumber, int64_t value) {
|
|
return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code int64} field, including tag.
|
|
*/
|
|
int32_t computeInt64Size(int32_t fieldNumber, int64_t value) {
|
|
return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code int32} field, including tag.
|
|
*/
|
|
int32_t computeInt32Size(int32_t fieldNumber, int32_t value) {
|
|
return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code fixed64} field, including tag.
|
|
*/
|
|
int32_t computeFixed64Size(int32_t fieldNumber, int64_t value) {
|
|
return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code fixed32} field, including tag.
|
|
*/
|
|
int32_t computeFixed32Size(int32_t fieldNumber, int32_t value) {
|
|
return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code bool} field, including tag.
|
|
*/
|
|
int32_t computeBoolSize(int32_t fieldNumber, BOOL value) {
|
|
return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code string} field, including tag.
|
|
*/
|
|
int32_t computeStringSize(int32_t fieldNumber, NSString* value) {
|
|
return computeTagSize(fieldNumber) + computeStringSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code group} field, including tag.
|
|
*/
|
|
int32_t computeGroupSize(int32_t fieldNumber, id<PBMessage> value) {
|
|
return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code group} field represented by an {@code PBUnknownFieldSet}, including
|
|
* tag.
|
|
*/
|
|
int32_t computeUnknownGroupSize(int32_t fieldNumber,
|
|
PBUnknownFieldSet* value) {
|
|
return computeTagSize(fieldNumber) * 2 + computeUnknownGroupSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* embedded message field, including tag.
|
|
*/
|
|
int32_t computeMessageSize(int32_t fieldNumber, id<PBMessage> value) {
|
|
return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code bytes} field, including tag.
|
|
*/
|
|
int32_t computeDataSize(int32_t fieldNumber, NSData* value) {
|
|
return computeTagSize(fieldNumber) + computeDataSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* {@code uint32} field, including tag.
|
|
*/
|
|
int32_t computeUInt32Size(int32_t fieldNumber, int32_t value) {
|
|
return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* enum field, including tag. Caller is responsible for converting the
|
|
* enum value to its numeric value.
|
|
*/
|
|
int32_t computeEnumSize(int32_t fieldNumber, int32_t value) {
|
|
return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code sfixed32} field, including tag.
|
|
*/
|
|
int32_t computeSFixed32Size(int32_t fieldNumber, int32_t value) {
|
|
return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code sfixed64} field, including tag.
|
|
*/
|
|
int32_t computeSFixed64Size(int32_t fieldNumber, int64_t value) {
|
|
return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code sint32} field, including tag.
|
|
*/
|
|
int32_t computeSInt32Size(int32_t fieldNumber, int32_t value) {
|
|
return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* {@code sint64} field, including tag.
|
|
*/
|
|
int32_t computeSInt64Size(int32_t fieldNumber, int64_t value) {
|
|
return computeTagSize(fieldNumber) +
|
|
computeRawVarint64Size(encodeZigZag64(value));
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a
|
|
* MessageSet extension to the stream. For historical reasons,
|
|
* the wire format differs from normal fields.
|
|
*/
|
|
int32_t computeMessageSetExtensionSize(int32_t fieldNumber, id<PBMessage> value) {
|
|
return computeTagSize(PBWireFormatMessageSetItem) * 2 +
|
|
computeUInt32Size(PBWireFormatMessageSetTypeId, fieldNumber) +
|
|
computeMessageSize(PBWireFormatMessageSetMessage, value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode an
|
|
* unparsed MessageSet extension field to the stream. For
|
|
* historical reasons, the wire format differs from normal fields.
|
|
*/
|
|
int32_t computeRawMessageSetExtensionSize(int32_t fieldNumber, NSData* value) {
|
|
return computeTagSize(PBWireFormatMessageSetItem) * 2 +
|
|
computeUInt32Size(PBWireFormatMessageSetTypeId, fieldNumber) +
|
|
computeDataSize(PBWireFormatMessageSetMessage, value);
|
|
}
|
|
|
|
|
|
/**
|
|
* Internal helper that writes the current buffer to the output. The
|
|
* buffer position is reset to its initial value when this returns.
|
|
*/
|
|
- (void) refreshBuffer {
|
|
if (output == nil) {
|
|
// We're writing to a single buffer.
|
|
@throw [NSException exceptionWithName:@"OutOfSpace" reason:@"" userInfo:nil];
|
|
}
|
|
|
|
|
|
[output write:buffer.bytes maxLength:(NSUInteger)position];
|
|
position = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Flushes the stream and forces any buffered bytes to be written. This
|
|
* does not flush the underlying OutputStream.
|
|
*/
|
|
- (void) flush {
|
|
if (output != nil) {
|
|
[self refreshBuffer];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* If writing to a flat array, return the space left in the array.
|
|
* Otherwise, throws {@code UnsupportedOperationException}.
|
|
*/
|
|
- (int32_t) spaceLeft {
|
|
if (output == nil) {
|
|
return (int32_t)buffer.length - position;
|
|
} else {
|
|
@throw [NSException exceptionWithName:@"UnsupportedOperation"
|
|
reason:@"spaceLeft() can only be called on CodedOutputStreams that are writing to a flat array."
|
|
userInfo:nil];
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Verifies that {@link #spaceLeft()} returns zero. It's common to create
|
|
* a byte array that is exactly big enough to hold a message, then write to
|
|
* it with a {@code PBCodedOutputStream}. Calling {@code checkNoSpaceLeft()}
|
|
* after writing verifies that the message was actually as big as expected,
|
|
* which can help catch bugs.
|
|
*/
|
|
- (void) checkNoSpaceLeft {
|
|
if (self.spaceLeft != 0) {
|
|
@throw [NSException exceptionWithName:@"IllegalState" reason:@"Did not write as much data as expected." userInfo:nil];
|
|
}
|
|
}
|
|
|
|
|
|
/** Write a single byte. */
|
|
- (void) writeRawByte:(uint8_t) value {
|
|
if (position == (int32_t)buffer.length) {
|
|
[self refreshBuffer];
|
|
}
|
|
|
|
((uint8_t*)buffer.mutableBytes)[position++] = value;
|
|
}
|
|
|
|
|
|
/** Write an array of bytes. */
|
|
- (void) writeRawData:(NSData*) data {
|
|
[self writeRawData:data offset:0 length:(int32_t)data.length];
|
|
}
|
|
|
|
|
|
- (void) writeRawData:(NSData*) value offset:(int32_t) offset length:(int32_t) length {
|
|
if ((int32_t)buffer.length - position >= length) {
|
|
// We have room in the current buffer.
|
|
memcpy(((uint8_t*)buffer.mutableBytes) + position, ((uint8_t*)value.bytes) + offset, length);
|
|
position += length;
|
|
} else {
|
|
// Write extends past current buffer. Fill the rest of this buffer and flush.
|
|
int32_t bytesWritten = (int32_t)buffer.length - position;
|
|
memcpy(((uint8_t*)buffer.mutableBytes) + position, ((uint8_t*)value.bytes) + offset, bytesWritten);
|
|
offset += bytesWritten;
|
|
length -= bytesWritten;
|
|
position = (int32_t)buffer.length;
|
|
[self refreshBuffer];
|
|
|
|
// Now deal with the rest.
|
|
// Since we have an output stream, this is our buffer
|
|
// and buffer offset == 0
|
|
if (length <= (int32_t)buffer.length) {
|
|
// Fits in new buffer.
|
|
memcpy((uint8_t*)buffer.mutableBytes, ((uint8_t*)value.bytes) + offset, length);
|
|
position = length;
|
|
} else {
|
|
// Write is very big. Let's do it all at once.
|
|
[output write:((uint8_t*) value.bytes) + offset maxLength:(NSUInteger)length];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** Encode and write a tag. */
|
|
- (void) writeTag:(int32_t) fieldNumber
|
|
format:(int32_t) format {
|
|
[self writeRawVarint32:PBWireFormatMakeTag(fieldNumber, format)];
|
|
}
|
|
|
|
|
|
/** Compute the number of bytes that would be needed to encode a tag. */
|
|
int32_t computeTagSize(int32_t fieldNumber) {
|
|
return computeRawVarint32Size(PBWireFormatMakeTag(fieldNumber, 0));
|
|
}
|
|
|
|
|
|
/**
|
|
* Encode and write a varint. {@code value} is treated as
|
|
* unsigned, so it won't be sign-extended if negative.
|
|
*/
|
|
- (void) writeRawVarint32:(int32_t) value {
|
|
while (YES) {
|
|
if ((value & ~0x7F) == 0) {
|
|
[self writeRawByte:(uint8_t)value];
|
|
return;
|
|
} else {
|
|
[self writeRawByte:((value & 0x7F) | 0x80)];
|
|
value = logicalRightShift32(value, 7);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Compute the number of bytes that would be needed to encode a varint.
|
|
* {@code value} is treated as unsigned, so it won't be sign-extended if
|
|
* negative.
|
|
*/
|
|
int32_t computeRawVarint32Size(int32_t value) {
|
|
if ((value & (0xffffffffLL << 7)) == 0) return 1;
|
|
if ((value & (0xffffffffLL << 14)) == 0) return 2;
|
|
if ((value & (0xffffffffLL << 21)) == 0) return 3;
|
|
if ((value & (0xffffffffLL << 28)) == 0) return 4;
|
|
return 5;
|
|
}
|
|
|
|
|
|
/** Encode and write a varint. */
|
|
- (void) writeRawVarint64:(int64_t) value{
|
|
while (YES) {
|
|
if ((value & ~0x7FL) == 0) {
|
|
[self writeRawByte:(uint8_t)((int32_t) value)];
|
|
return;
|
|
} else {
|
|
[self writeRawByte:(uint8_t)(((int32_t) value & 0x7F) | 0x80)];
|
|
value = logicalRightShift64(value, 7);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/** Compute the number of bytes that would be needed to encode a varint. */
|
|
int32_t computeRawVarint64Size(int64_t value) {
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 7)) == 0) return 1;
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 14)) == 0) return 2;
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 21)) == 0) return 3;
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 28)) == 0) return 4;
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 35)) == 0) return 5;
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 42)) == 0) return 6;
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 49)) == 0) return 7;
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 56)) == 0) return 8;
|
|
if (((uint64_t)value & (0xffffffffffffffffULL << 63)) == 0) return 9;
|
|
return 10;
|
|
}
|
|
|
|
|
|
/** Write a little-endian 32-bit integer. */
|
|
- (void) writeRawLittleEndian32:(int32_t) value {
|
|
[self writeRawByte:((value ) & 0xFF)];
|
|
[self writeRawByte:((value >> 8) & 0xFF)];
|
|
[self writeRawByte:((value >> 16) & 0xFF)];
|
|
[self writeRawByte:((value >> 24) & 0xFF)];
|
|
}
|
|
|
|
|
|
/** Write a little-endian 64-bit integer. */
|
|
- (void) writeRawLittleEndian64:(int64_t) value {
|
|
[self writeRawByte:((int32_t)(value ) & 0xFF)];
|
|
[self writeRawByte:((int32_t)(value >> 8) & 0xFF)];
|
|
[self writeRawByte:((int32_t)(value >> 16) & 0xFF)];
|
|
[self writeRawByte:((int32_t)(value >> 24) & 0xFF)];
|
|
[self writeRawByte:((int32_t)(value >> 32) & 0xFF)];
|
|
[self writeRawByte:((int32_t)(value >> 40) & 0xFF)];
|
|
[self writeRawByte:((int32_t)(value >> 48) & 0xFF)];
|
|
[self writeRawByte:((int32_t)(value >> 56) & 0xFF)];
|
|
}
|
|
|
|
|
|
/**
|
|
* Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
|
|
* into values that can be efficiently encoded with varint. (Otherwise,
|
|
* negative values must be sign-extended to 64 bits to be varint encoded,
|
|
* thus always taking 10 bytes on the wire.)
|
|
*
|
|
* @param n A signed 32-bit integer.
|
|
* @return An unsigned 32-bit integer, stored in a signed int
|
|
*/
|
|
int32_t encodeZigZag32(int32_t n) {
|
|
// Note: the right-shift must be arithmetic
|
|
return (n << 1) ^ (n >> 31);
|
|
}
|
|
|
|
|
|
/**
|
|
* Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
|
|
* into values that can be efficiently encoded with varint. (Otherwise,
|
|
* negative values must be sign-extended to 64 bits to be varint encoded,
|
|
* thus always taking 10 bytes on the wire.)
|
|
*
|
|
* @param n A signed 64-bit integer.
|
|
* @return An unsigned 64-bit integer, stored in a signed int
|
|
*/
|
|
int64_t encodeZigZag64(int64_t n) {
|
|
// Note: the right-shift must be arithmetic
|
|
return (n << 1) ^ (n >> 63);
|
|
}
|
|
|
|
@end
|