blob: 63aaa1c2c7ad99ecd063058d06bdd59250512421 [file] [log] [blame] [edit]
//
// Copyright 2019 Google Inc.
//
// 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 "Channel/Sources/EDOChannelUtil.h"
static const uint64_t kGEDOSocketFrameHeaderTag = 0xc080c080;
/**
* The data header for each data package being sent.
*
* The header data layout:
* |--- 32bit ---|--- 32bit ---|----- 32 bit -----|--- flexible ---|
* |-- type(1) --|- 0xc080c080-|- length of data -|--*-* data *-*--|
*/
typedef struct EDOSocketFrameHeader_s {
// Type of frame, always 1.
uint32_t type;
// Tag.
uint32_t tag;
// If payloadSize is larger than zero, @c payloadSize of bytes are following.
uint32_t payloadSize;
} __attribute__((__packed__)) EDOSocketFrameHeader_t;
// Check if the frame header is valid
// TODO(haowoo): add more checksum checks.
static BOOL edo_isFrameHeaderValid(const EDOSocketFrameHeader_t *header) {
// Make sure it is not NULL and the tag matches the magic tag so we can make sure the data being
// processed is in the expected format.
return header != NULL && header->tag == kGEDOSocketFrameHeaderTag;
}
size_t EDOGetPayloadHeaderSize(void) { return sizeof(EDOSocketFrameHeader_t); }
size_t EDOGetPayloadSizeFromFrameData(dispatch_data_t data) {
if (data == NULL) {
return 0;
}
__block size_t payloadSize = 0;
dispatch_data_t contiguousData = dispatch_data_create_map(data, NULL, NULL);
dispatch_data_apply(contiguousData, ^bool(dispatch_data_t region, size_t offset,
const void *buffer, size_t size) {
const struct EDOSocketFrameHeader_s *frame = buffer;
if (offset == 0 && edo_isFrameHeaderValid(frame)) {
payloadSize = ntohl(frame->payloadSize);
}
return YES;
});
return payloadSize;
}
dispatch_data_t EDOBuildFrameFromDataWithQueue(NSData *data, dispatch_queue_t queue) {
dispatch_data_t frameData = dispatch_data_create(data.bytes, data.length, queue, ^{
// The trick to have the block capture and retain the data.
[data length];
});
dispatch_data_t headerData = ({
EDOSocketFrameHeader_t frameHeader = {
.type = 1,
.tag = kGEDOSocketFrameHeaderTag,
.payloadSize = htonl(data.length),
};
NSData *headerData = [NSData dataWithBytes:&frameHeader length:sizeof(frameHeader)];
dispatch_data_create(headerData.bytes, headerData.length, queue, ^{
[headerData length];
});
});
return dispatch_data_create_concat(headerData, frameData);
}