mirror of https://github.com/ARMmbed/mbed-os.git
324 lines
7.4 KiB
C++
324 lines
7.4 KiB
C++
/* mbed Microcontroller Library
|
|
* Copyright (c) 2018 ARM Limited
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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>
|
|
|
|
#include "nfc/ndef/MessageBuilder.h"
|
|
|
|
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(
|
|
const RecordType &type,
|
|
const RecordPayload &payload,
|
|
bool is_last_record
|
|
)
|
|
{
|
|
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
|
|
)
|
|
{
|
|
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)
|
|
{
|
|
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)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (record.type.tnf == RecordType::well_known_type ||
|
|
record.type.tnf == RecordType::media_type ||
|
|
record.type.tnf == RecordType::absolute_uri ||
|
|
record.type.tnf == RecordType::external_type
|
|
) {
|
|
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);
|
|
if (record_size > (_message_buffer.size() - _position)) {
|
|
return false;
|
|
}
|
|
|
|
append_header(record, builder);
|
|
append_type_length(record);
|
|
append_payload_length(record, builder);
|
|
append_id_length(record);
|
|
append_type(record);
|
|
append_id(record);
|
|
append_payload(record, builder);
|
|
|
|
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)
|
|
{
|
|
size_t record_size = 0;
|
|
record_size = 1; /* header */
|
|
record_size += 1; /* type length */
|
|
record_size += is_short_payload(record, builder) ? 1 : 4;
|
|
|
|
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);
|
|
|
|
return record_size;
|
|
}
|
|
|
|
void MessageBuilder::append_header(const Record &record, const PayloadBuilder *builder)
|
|
{
|
|
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)) {
|
|
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)
|
|
{
|
|
size_t size = get_payload_size(record, builder);
|
|
|
|
if (is_short_payload(record, builder)) {
|
|
_message_buffer[_position++] = size;
|
|
} 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)
|
|
{
|
|
size_t size = get_payload_size(record, builder);
|
|
if (!size) {
|
|
return;
|
|
}
|
|
|
|
if (builder) {
|
|
builder->build(_message_buffer.subspan(_position, size));
|
|
} else {
|
|
memcpy(
|
|
_message_buffer.data() + _position,
|
|
record.payload.data(),
|
|
size
|
|
);
|
|
}
|
|
|
|
_position += size;
|
|
}
|
|
|
|
bool MessageBuilder::is_short_payload(const Record &record, const PayloadBuilder *builder)
|
|
{
|
|
if (get_payload_size(record, builder) <= 255) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
size_t MessageBuilder::get_payload_size(const Record &record, const PayloadBuilder *builder)
|
|
{
|
|
return builder ? builder->size() : record.payload.size();
|
|
}
|
|
|
|
} // namespace ndef
|
|
} // namespace nfc
|
|
} // namespace mbed
|
|
|