mbed-os/connectivity/nfc/source/ndef/MessageBuilder.cpp

324 lines
7.4 KiB
C++
Raw Normal View History

2018-08-24 15:16:37 +00:00
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
* SPDX-License-Identifier: Apache-2.0
2018-08-24 15:16:37 +00:00
*
* 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.
*/
#include <string.h>
2018-08-24 15:16:37 +00:00
2018-08-24 16:44:03 +00:00
#include "nfc/ndef/MessageBuilder.h"
2018-08-24 15:16:37 +00:00
namespace mbed {
namespace nfc {
namespace ndef {
MessageBuilder::MessageBuilder(const Span<uint8_t> &buffer) :
_message_buffer(buffer),
_position(0),
_message_started(false),
_message_ended(false),
_in_chunk(false)
{ }
bool MessageBuilder::append_record(
2018-08-24 16:59:30 +00:00
const RecordType &type,
const RecordPayload &payload,
bool is_last_record
)
{
2018-08-24 15:16:37 +00:00
Record record(
type,
payload,
/* id */ RecordID(),
/* chunk */ false,
is_last_record
);
return append_record(record);
}
bool MessageBuilder::append_record(
const RecordType &type,
const PayloadBuilder &builder,
bool is_last_record
2018-08-29 15:59:45 +00:00
)
{
Record record(
type,
RecordPayload(),
RecordID(),
/* chunk */ false,
is_last_record
);
return append_record(record, &builder);
}
bool MessageBuilder::append_record(const Record &record, const PayloadBuilder *builder)
2018-08-24 15:16:37 +00:00
{
if (_message_ended) {
return false;
}
if (record.type.value.size() > 255) {
return false;
}
if (record.id.size() > 255) {
return false;
}
if (!record.id.empty() && _in_chunk) {
if (record.chunk) {
// middle chunk
return false;
} else if (record.type.tnf == RecordType::unchanged) {
// terminating chunk
return false;
}
}
if (_in_chunk && record.type.tnf != RecordType::unchanged) {
return false;
}
if (!_in_chunk && record.chunk && record.type.tnf == RecordType::unchanged) {
return false;
}
if (record.type.tnf == RecordType::empty) {
if (!record.type.value.empty()) {
return false;
}
if (!record.id.empty()) {
return false;
}
if (get_payload_size(record, builder)) {
2018-08-24 15:16:37 +00:00
return false;
}
}
if (record.type.tnf == RecordType::well_known_type ||
2018-08-24 16:59:30 +00:00
record.type.tnf == RecordType::media_type ||
record.type.tnf == RecordType::absolute_uri ||
record.type.tnf == RecordType::external_type
) {
2018-08-24 15:16:37 +00:00
if (record.type.value.empty()) {
return false;
}
}
if (record.type.tnf == RecordType::unknown && !record.type.value.empty()) {
return false;
}
size_t record_size = compute_record_size(record, builder);
2018-08-24 15:16:37 +00:00
if (record_size > (_message_buffer.size() - _position)) {
return false;
}
append_header(record, builder);
2018-08-24 15:16:37 +00:00
append_type_length(record);
append_payload_length(record, builder);
2018-08-24 15:16:37 +00:00
append_id_length(record);
append_type(record);
append_id(record);
append_payload(record, builder);
2018-08-24 15:16:37 +00:00
if (record.chunk) {
_in_chunk = true;
} else if (record.type.tnf == RecordType::unchanged) {
// last chunk reached
_in_chunk = false;
}
return true;
}
void MessageBuilder::reset()
{
_position = 0;
_message_started = false;
_message_ended = false;
_in_chunk = false;
}
void MessageBuilder::reset(const Span<uint8_t> &buffer)
{
_message_buffer = buffer;
_position = 0;
_message_started = false;
_message_ended = false;
_in_chunk = false;
}
bool MessageBuilder::is_message_complete() const
{
return _message_ended;
}
Span<const uint8_t> MessageBuilder::get_message() const
{
if (is_message_complete()) {
return _message_buffer.first(_position);
} else {
return Span<const uint8_t>();
}
}
size_t MessageBuilder::compute_record_size(const Record &record, const PayloadBuilder *builder)
2018-08-24 15:16:37 +00:00
{
size_t record_size = 0;
record_size = 1; /* header */
record_size += 1; /* type length */
record_size += is_short_payload(record, builder) ? 1 : 4;
2018-08-24 15:16:37 +00:00
if (!record.id.empty()) {
record_size += 1;
}
record_size += record.type.value.size();
record_size += record.id.size();
record_size += get_payload_size(record, builder);
2018-08-24 15:16:37 +00:00
return record_size;
}
void MessageBuilder::append_header(const Record &record, const PayloadBuilder *builder)
2018-08-24 15:16:37 +00:00
{
uint8_t header = 0;
if (!_message_started) {
header |= Header::message_begin_bit;
_message_started = true;
}
if (record.last_record) {
header |= Header::message_end_bit;
_message_ended = true;
}
if (record.chunk) {
header |= Header::chunk_flag_bit;
}
if (is_short_payload(record, builder)) {
2018-08-24 15:16:37 +00:00
header |= Header::short_record_bit;
}
if (record.id.size()) {
header |= Header::id_length_bit;
}
header |= record.type.tnf;
_message_buffer[_position++] = header;
}
void MessageBuilder::append_type_length(const Record &record)
{
_message_buffer[_position++] = record.type.value.size();
}
void MessageBuilder::append_payload_length(const Record &record, const PayloadBuilder *builder)
2018-08-24 15:16:37 +00:00
{
size_t size = get_payload_size(record, builder);
2018-08-24 15:16:37 +00:00
if (is_short_payload(record, builder)) {
_message_buffer[_position++] = size;
2018-08-24 15:16:37 +00:00
} else {
_message_buffer[_position++] = (size >> 24) & 0xFF;
_message_buffer[_position++] = (size >> 16) & 0xFF;
_message_buffer[_position++] = (size >> 8) & 0xFF;
_message_buffer[_position++] = size & 0xFF;
}
}
void MessageBuilder::append_id_length(const Record &record)
{
if (record.id.empty()) {
return;
}
_message_buffer[_position++] = record.id.size();
}
void MessageBuilder::append_type(const Record &record)
{
if (record.type.value.empty()) {
return;
}
memcpy(
_message_buffer.data() + _position,
record.type.value.data(),
record.type.value.size()
);
_position += record.type.value.size();
}
void MessageBuilder::append_id(const Record &record)
{
if (record.id.empty()) {
return;
}
memcpy(
_message_buffer.data() + _position,
record.id.data(),
record.id.size()
);
_position += record.id.size();
}
void MessageBuilder::append_payload(const Record &record, const PayloadBuilder *builder)
2018-08-24 15:16:37 +00:00
{
size_t size = get_payload_size(record, builder);
if (!size) {
2018-08-24 15:16:37 +00:00
return;
}
if (builder) {
builder->build(_message_buffer.subspan(_position, size));
} else {
memcpy(
_message_buffer.data() + _position,
record.payload.data(),
size
);
}
_position += size;
2018-08-24 15:16:37 +00:00
}
bool MessageBuilder::is_short_payload(const Record &record, const PayloadBuilder *builder)
2018-08-24 15:16:37 +00:00
{
if (get_payload_size(record, builder) <= 255) {
2018-08-24 15:16:37 +00:00
return true;
} else {
return false;
}
}
size_t MessageBuilder::get_payload_size(const Record &record, const PayloadBuilder *builder)
{
return builder ? builder->size() : record.payload.size();
}
2018-08-24 15:16:37 +00:00
} // namespace ndef
} // namespace nfc
} // namespace mbed