NFC: Add NDEF message framework.

pull/7822/head
Vincent Coubard 2018-08-24 16:16:37 +01:00 committed by Donatien Garnier
parent 61445cc9af
commit dcf38eec50
15 changed files with 2598 additions and 0 deletions

View File

@ -0,0 +1,136 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#ifndef NFC_COMMON_MIME_H_
#define NFC_COMMON_MIME_H_
#include "platform/Span.h"
#include <nfc/ndef/RecordParser.h>
#include <nfc/ndef/MessageBuilder.h>
#include <stdint.h>
namespace mbed {
namespace nfc {
namespace common {
/**
* @addtogroup nfc
* @{
*/
/**
* Represent a mime object.
*/
class Mime {
public:
/**
* Construct an empty Mime object.
*/
Mime();
/**
* Construct a mime object from its type and content
*
* @param mime_type The mime type of the object
* @param content The content of the object.
*/
Mime(
const Span<const uint8_t> &mime_type,
const Span<const uint8_t> &content
);
/**
* Copy construct a Mime object.
* @param other The Mime object copied.
*/
Mime(const Mime &other);
/**
* Destroy a Mime object.
*/
~Mime();
/**
* Copy asign a Mime object.
* @param to_copy The Mime object to copy.
* @return a reference to this object
*/
Mime& operator=(const Mime &other);
/**
* Set all attributes of a mime object.
* @param mime_type Type of the mime object.
* @param content Content of the mime object.
*/
void set_mime(
const Span<const uint8_t> &mime_type,
const Span<const uint8_t> &content
);
/**
* Return the mime type.
* @return The mime type.
*/
Span<const uint8_t> get_mime_type() const;
/**
* Return the content of thr mime object.
* @return the content of the mime object.
*/
Span<const uint8_t> get_mime_content() const;
/**
* Append into a message builder
*/
bool append_as_record(
ndef::MessageBuilder &message_builder,
bool is_last_record = false
);
private:
friend class MimeParser;
void move_data(
uint8_t *mime_record,
size_t mime_type_size,
size_t mime_content_size
);
size_t mime_size() const;
uint8_t *_mime;
size_t _type_size;
size_t _content_size;
};
/**
* Parse a Mime payload.
*/
class MimeParser : public ndef::GenericRecordParser<MimeParser, Mime> {
public:
bool do_parse(const ndef::Record &record, Mime &mime);
};
/**
* @}
*/
} // namespace common
} // namespace nfc
} // namespace mbed
#endif /* NFC_COMMON_MIME_H_ */

View File

@ -0,0 +1,167 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#ifndef NFC_COMMON_SIMPLEMESSAGEPARSER_H_
#define NFC_COMMON_SIMPLEMESSAGEPARSER_H_
#include "platform/Span.h"
#include "nfc/ndef/MessageParser.h"
#include "nfc/ndef/RecordParser.h"
#include "URI.h"
#include "Text.h"
#include "Mime.h"
namespace mbed {
namespace nfc {
namespace common {
/**
* @addtogroup nfc
* @{
*/
/**
* Basic message parser that aggregates URIParser, TextParser and MimeParser.
*
* Custom parsers can be added at runtime as well.
*/
class SimpleMessageParser :
ndef::MessageParser::Delegate,
URIParser::Delegate,
TextParser::Delegate,
MimeParser::Delegate
{
public:
/**
* Delegate invoked when the parser raise an event.
*/
struct Delegate {
/**
* Invoked when an error is present in the message.
* @param error The error present in the message.
*/
virtual void on_parsing_error(ndef::MessageParser::error_t error) { }
/**
* Invoked when parsing as started.
*/
virtual void on_parsing_started() { }
/**
* Invoked when a text element has been parsed.
* @param text The text parsed.
*/
virtual void on_text_parsed(const Text &text, const ndef::RecordID &id) { }
/**
* Invoked when a text element has been parsed.
* @param text The text parsed.
*/
virtual void on_uri_parsed(const URI &uri, const ndef::RecordID &id) { }
/**
* Invoked when a mime element has been parsed.
* @param mime The mime object parsed.
*/
virtual void on_mime_parsed(const Mime &mime, const ndef::RecordID &id) { }
/**
* Invoked when an unknown record has been parsed.
* @param The record freshly parsed.
*/
virtual void on_unknown_record_parsed(const ndef::Record &record) { }
/**
* Invoked when parsing is over.
*/
virtual void on_parsing_terminated() { }
protected:
~Delegate() { }
};
/**
* Construct a new CommonMessageParser.
*/
SimpleMessageParser();
/**
* Set the handler that processes parsing events.
* @param delegate The parsing event handler.
*/
void set_delegate(Delegate *delegate);
/**
* Parse an NDEF Message.
*
* Records and errors are reported to the handler registered with
* set_event_handler.
*
* @param data_buffer The data buffer that contains the NDEF message.
*/
void parse(const Span<const uint8_t> &data_buffer);
/**
* Insert a new parser in the parser chain.
* @param parser The parser to add in the parsing chain.
*/
void add_record_parser(ndef::RecordParser *parser);
private:
////////////////////////////////////////////////////////////////////////////
/// Implementation of MessageParser::EventHandler
virtual void on_parsing_error(ndef::MessageParser::error_t error);
virtual void on_parsing_started();
virtual void on_record_parsed(const ndef::Record &record);
virtual void on_parsing_terminated();
////////////////////////////////////////////////////////////////////////////
/// Implementation of URIParser::EventHandler
virtual void on_record_parsed(const URI &uri, const ndef::RecordID &id);
////////////////////////////////////////////////////////////////////////////
/// Implementation of TextParser::EventHandler
virtual void on_record_parsed(const Text &text, const ndef::RecordID &id);
////////////////////////////////////////////////////////////////////////////
/// Implementation of MimeParser::EventHandler
virtual void on_record_parsed(const Mime &mime, const ndef::RecordID &id);
ndef::MessageParser _message_parser;
ndef::RecordParserChain _record_parser_chain;
URIParser _uri_parser;
TextParser _text_parser;
MimeParser _mime_parser;
Delegate *_delegate;
};
/**
* @}
*/
} // namespace common
} // namespace nfc
} // namespace mbed
#endif /* NFC_COMMON_SIMPLEMESSAGEPARSER_H_ */

View File

@ -0,0 +1,145 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#ifndef NFC_COMMON_TEXT_H_
#define NFC_COMMON_TEXT_H_
#include "platform/Span.h"
#include <nfc/ndef/RecordParser.h>
#include <nfc/ndef/MessageBuilder.h>
#include <stdint.h>
namespace mbed {
namespace nfc {
namespace common {
/**
* @addtogroup nfc
* @{
*/
/**
* Represent the well known type text.
*/
class Text {
public:
/**
* Encoding of the text.
*/
enum encoding_t {
UTF8 = 0,//!< UTF8
UTF16 = 1//!< UTF16
};
/**
* Construct an empty text element.
*/
Text();
/**
* Construct a text element from a data buffer and an encoding.
* @param text_encoding The encoding of the text.
* @param language_code The string of the language code.
* @param text The text buffer.
*/
Text(
encoding_t text_encoding,
const Span<const uint8_t> &language_code,
const Span<const uint8_t> &text
);
/**
* Copy construct a text element.
* @param to_copy
*/
Text(const Text &to_copy);
/**
* Destroy a text element.
*/
~Text();
/**
* Copy assignment of another text element.
* @param to_copy The Text instance to copy
* @return a reference to this object.
*/
Text& operator=(const Text &to_copy);
/**
* Copy a text from an external buffer.
* @param language_code The language code of the text.
* @param text The text to copy.
*/
void set_text(
encoding_t text_encoding,
const Span<const uint8_t> &language_code,
const Span<const uint8_t> &text
);
/**
* Get the encoding of the text.
* @return The encoding of the text.
*/
encoding_t get_encoding() const;
/**
* Return the language code.
* @return The language code.
*/
Span<const uint8_t> get_language_code() const;
/**
* Return the text contained in this object.
* @return The text contained in this object.
*/
Span<const uint8_t> get_text() const;
/**
* Append into a message builder
*/
bool append_as_record(
ndef::MessageBuilder &message_builder,
bool is_last_record = false
);
private:
friend class TextParser;
void move_data(uint8_t *text, size_t size);
uint8_t *_text_record;
size_t _text_record_size;
};
/**
* Parse a Text.
*/
class TextParser : public ndef::GenericRecordParser<TextParser, Text> {
public:
virtual bool do_parse(const ndef::Record &record, Text &text);
};
/**
* @}
*/
} // namespace common
} // namespace nfc
} // namespace mbed
#endif /* NFC_COMMON_TEXT_H_ */

View File

@ -0,0 +1,189 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#ifndef NFC_COMMON_URI_H_
#define NFC_COMMON_URI_H_
#include <nfc/ndef/RecordParser.h>
#include <nfc/ndef/MessageBuilder.h>
#include <stdint.h>
#include <string.h>
#include <platform/Span.h>
namespace mbed {
namespace nfc {
namespace common {
/**
* @addtogroup nfc
* @{
*/
/**
* Model the well known type URI.
*/
class URI {
public:
/**
* Identifier codes
*/
enum uri_identifier_code_t {
NA = 0x00, /// Not applicable
HTTP_WWW = 0x01, /// http://www.
HTTPS_WWW = 0x02, /// https://www.
HTTP = 0x03, /// http://
HTTPS = 0x04, /// https://
TEL = 0x05, /// tel:
MAILTO = 0x06, /// mailto:
FTP_ANONYMOUS = 0x07, /// ftp://anonymous:anonymous@
FTP_FTP = 0x08, /// ftp://ftp.
FTPS = 0x09, /// ftps://
SFTP = 0x0A, /// sftp://
SMB = 0x0B, /// smb://
NFS = 0x0C, /// nfs://
FTP = 0x0D, /// ftp://
DAV = 0x0E, /// dav://
NEWS = 0x0F, /// news:
TELNET = 0x10, /// telnet://
IMAP = 0x11, /// imap:
RSTP = 0x12, /// rstp://
URN = 0x13, /// urn:
POP = 0x14, /// pop:
SIP = 0x15, /// sip:
SIPS = 0x16, /// sips:
TFTP = 0x17, /// tftp:
BTSPP = 0x18, /// btspp://
BTL2CAP = 0x19, /// btl2cap://
BTGOEP = 0x1A, /// btgoep://
TCPOBEX = 0x1B, /// tcpobex://
IRDAOBEX = 0x1C, /// irdaobex://
FILE = 0x1D, /// file://
URN_EPC_ID = 0x1E, /// urn:epc:id:
URN_EPC_TAG = 0x1F, /// urn:epc:tag:
URN_EPC_PAT = 0x20, /// urn:epc:pat:
URN_EPC_RAW = 0x21, /// urn:epc:raw:
URN_EPC = 0x22, /// urn:epc:
URN_NFC = 0x23, /// urn:nfc:
};
/**
* Construct an empty URI object.
*/
URI();
/**
* Construct a URI from an id and a uri field.
* @param id The code of the URI prefix.
* @param uri_field The URI itself.
* @param uri_field_size The size of the URI.
*/
URI(uri_identifier_code_t id, const Span<const uint8_t> &uri_field);
/**
* Construct a URI from another URI.
* @param to_copy The uri copied.
*/
URI(const URI &to_copy);
/**
* Destroy a URI object.
*/
~URI();
/**
* Replace the content by the one of an existing URI.
* @param to_copy The URI to copy.
* @return a reference to this object
*/
URI& operator=(const URI &to_copy);
/**
* Replace the value of the URI.
* @param id The ID of the URI
* @param uri_field A buffer containing the value of the URI field.
*/
void set_uri(
uri_identifier_code_t id,
const Span<const uint8_t> &uri_field
);
/**
* Return the id of the uri.
* @return The id of the uri.
*/
uri_identifier_code_t get_id() const;
/**
* Return the current value of the uri field.
* @return The value of the uri field.
*/
Span<const uint8_t> get_uri_field() const;
/**
* Append into a message builder
*/
bool append_as_record(
ndef::MessageBuilder &message_builder,
bool is_last_record = false
);
/**
* Equal operator between two URIs
* @param lhs The URI on the left hand side
* @param rhs The URI on the right hand side
* @return true if lhs equals rhs or false.
*/
friend bool operator==(const URI &lhs, const URI &rhs)
{
if (lhs._uri_size != rhs._uri_size) {
return false;
}
return memcmp(lhs._uri, rhs._uri, lhs._uri_size) == 0;
}
friend bool operator!=(const URI &lhs, const URI &rhs)
{
return !(lhs == rhs);
}
private:
friend class URIParser;
void move_data(uint8_t *text, size_t size);
uint8_t *_uri;
size_t _uri_size;
};
/**
* Parser of a URI.
*/
class URIParser : public ndef::GenericRecordParser<URIParser, URI> {
public:
bool do_parse(const ndef::Record &record, URI &uri);
};
/**
* @}
*/
} // namespace common
} // namespace nfc
} // namespace mbed
#endif /* NFC_COMMON_URI_H_ */

View File

@ -0,0 +1,138 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#ifndef NFC_NDEF_MESSAGEBUILDER_H_
#define NFC_NDEF_MESSAGEBUILDER_H_
#include <stdint.h>
#include "Record.h"
#include "platform/Span.h"
namespace mbed {
namespace nfc {
namespace ndef {
/**
* @addtogroup nfc
* @{
*/
/**
* Construct a NDEF Message.
*/
class MessageBuilder {
public:
/**
* Create a new MessageBuilder that can be used to construct valid NDEF
* messages.
*
* @param buffer The data buffer that will contain the NDEF message.
*/
MessageBuilder(const Span<uint8_t> &buffer);
/**
* Append a new record to the message being built.
*
* @param type The type of the record to insert.
* @param payload The payload of the record (optional).
* @param is_last_record true if the record to insert is the last record of
* the payload or false otherwise.
*
* @return true if the record has been successfully inserted or false
* otherwise.
*
* @note insertion can fail if the message is already complete or if the
* size remaining in the message buffer is not large enough to makes the
* record inserted fit.
*/
bool append_record(
const RecordType &type,
const RecordPayload &payload = RecordPayload(),
bool is_last_record = false
);
/**
* Append a new record to the message being built.
*
* @param record The record to insert.
*
* @return true if the record has been successfully inserted or false otherwise.
*
* @note insertion can fail if the message is already complete or if the
* size remaining in the message buffer is not large enough to makes the
* record inserted fit.
*/
bool append_record(const Record &record);
/**
* Reset the builder state.
*/
void reset();
/**
* Reset the builder state and assign a new buffer to it.
*/
void reset(const Span<uint8_t> &buffer);
/**
* Return true if the message stored is complete and false otherwise.
*
* @return true if the message is complete or false.
*/
bool is_message_complete() const;
/**
* Return the buffer storing the data if the message is complete or an empty
* buffer if the message is not complete.
*
* @return The message built.
*/
Span<const uint8_t> get_message() const;
private:
// append fields
void append_header(const Record &record);
void append_type_length(const Record &record);
void append_payload_length(const Record&);
void append_id_length(const Record&);
void append_type(const Record&);
void append_id(const Record&);
void append_payload(const Record&);
// helpers
static size_t compute_record_size(const Record &record);
static bool is_short_payload(const Record &record);
// builder state.
Span<uint8_t> _message_buffer;
size_t _position;
bool _message_started;
bool _message_ended;
bool _in_chunk;
};
/**
* @}
*/
} // namespace ndef
} // namespace nfc
} // namespace mbed
#endif /* NFC_NDEF_MESSAGEBUILDER_H_ */

View File

@ -0,0 +1,178 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#ifndef NFC_NDEF_MESSAGEPARSER_H_
#define NFC_NDEF_MESSAGEPARSER_H_
#include <stdlib.h>
#include "platform/Span.h"
namespace mbed {
namespace nfc {
namespace ndef {
/**
* @addtogroup nfc
* @{
*/
// Forward declaration
class Record;
/**
* Event driven NDEF Message parser
*/
class MessageParser {
public:
/**
* Error that can be reported by the parsing operation.
*/
enum error_t {
/**
* The message doesn't start with a message start tag.
*/
INVALID_MESSAGE_START,
/**
* There is not enough data left to pursue parsing of the message.
*/
INSUFICIENT_DATA,
/**
* The type name of a record is invalid.
*/
INVALID_TYPE_NAME_FORMAT,
/**
* An empty record is malformed.
*/
INVALID_EMPTY_RECORD,
/**
* Record of unknown type embed a type length different than 0.
*/
INVALID_UNKNOWN_TYPE_LENGTH,
/**
* Record of unchanged type contains a type.
*/
INVALID_UNCHANGED_TYPE,
/**
* Chunk record encountered.
*/
CHUNK_RECORD_NOT_SUPPORTED,
/**
* Message is not properly closed.
*/
MISSING_MESSAGE_END,
/**
* Type is missing in a record expecting a type (well known type, media
* type, absolute uri or external type).
*/
MISSING_TYPE_VALUE
};
/**
* Report parsing event to the application.
*/
struct Delegate {
/**
* Invoked when parsing as started.
*/
virtual void on_parsing_started() { }
/**
* Invoked when a record has been parsed.
*
* @param The record obtained from parsing.
*/
virtual void on_record_parsed(const Record &record) { }
/**
* Invoked when parsing is over.
*/
virtual void on_parsing_terminated() { }
/**
* Invoked when an error is present in the message.
* @param error The error present in the message.
*/
virtual void on_parsing_error(error_t error) { }
protected:
/**
* Protected non virtual destructor.
* Delegate is not meant to be destroyed in a polymorphic manner.
*/
~Delegate() { }
};
/**
* Construct a message parser.
*/
MessageParser();
/**
* Set the handler that processes parsing events.
* @param delegate The parsing event handler.
*/
void set_delegate(Delegate *delegate);
/**
* Parse an NDEF Message.
*
* Records and errors are reported to the handler registered with
* set_event_handler.
*
* @param data_buffer The data buffer that contains the NDEF message.
*/
void parse(const Span<const uint8_t> &data_buffer);
private:
struct parsing_state_t;
// parser
bool parse_record(parsing_state_t &it);
static uint8_t compute_lengths_size(uint8_t header);
static uint8_t extract_type_length(parsing_state_t &s);
static uint32_t extract_payload_length(parsing_state_t &s, uint8_t header);
static uint8_t extract_id_length(parsing_state_t &s, uint8_t header);
// reporting
void report_parsing_started();
void report_record_parsed(const Record &record);
void report_parsing_terminated();
void report_parsing_error(error_t error, parsing_state_t& parsing_state);
Delegate *_delegate;
};
/**
* @}
*/
} // namespace ndef
} // namespace nfc
} // namespace mbed
#endif /* NFC_NDEF_MESSAGEPARSER_H_ */

View File

@ -0,0 +1,213 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#ifndef NFC_NDEF_RECORD_H_
#define NFC_NDEF_RECORD_H_
#include <stdint.h>
#include "platform/Span.h"
namespace mbed {
namespace nfc {
namespace ndef {
/**
* @addtogroup nfc
* @{
*/
/**
* Set of constants of a record header
*/
struct Header {
static const uint8_t message_begin_bit = (1 << 7);
static const uint8_t message_end_bit = (1 << 6);
static const uint8_t chunk_flag_bit = (1 << 5);
static const uint8_t short_record_bit = (1 << 4);
static const uint8_t id_length_bit = (1 << 3);
static const uint8_t tnf_bits = (1 << 0) | (1 << 1) | (1 << 2);
};
/**
* Encode a record type.
*
* A RecordType is composed of a type name format flag and an optional type
* value.
*/
struct RecordType {
enum tnf_t {
/**
* empty type; value must be empty.
*/
empty = 0x00,
/**
* Type defined by the NFC forum; value must be defined.
*/
well_known_type = 0x01,
/**
* Mime type; value must be defined.
*/
media_type = 0x02,
/**
* Absolute URI; value must be defined.
*/
absolute_uri = 0x03,
/**
* Type defined by vendors; value must be defined.
*/
external_type = 0x04,
/**
* Unknown type; value must be empty.
*/
unknown = 0x05,
/**
* Use for middle and terminating chunk record.
* value must be empty.
*/
unchanged = 0x06
};
/**
* Construct an unknown type.
*/
RecordType() : tnf(unknown), value() { }
/**
* Construct a type with no value.
*
* @note Valid tnf are: empty, unknown and unchanged.
*
* @param tnf The type name format of the type.
*/
RecordType(tnf_t tnf) :
tnf(tnf), value()
{ }
/**
* Construct a RecordType from a type name format and its associated value.
*
* @param tnf The type name format of the record type.
* @param value The value associated with the tnf.
*/
RecordType(tnf_t tnf, const Span<const uint8_t> &value) :
tnf(tnf), value(value)
{ }
/**
* Type name format of the record type.
*/
tnf_t tnf;
/**
* Value associated with the record type. It can be empty.
*/
Span<const uint8_t> value;
};
/**
* Definition of a Record payload.
*
* @note A payload can be empty.
*/
typedef Span<const uint8_t> RecordPayload;
/**
* Definition of a Record IR.
*
* @note ID's are optional and therefore it can be empty.
*/
typedef Span<const uint8_t> RecordID;
/**
* Represent a record.
*/
struct Record {
/**
* Construct an empty record.
*/
Record() : type(), payload(), id(), chunk(false), last_record(false) { }
/**
* Construct a record from its type, payload and id.
*
* The flags chunk and last_record can be added to indicate if the record
* is aprt of a chunk or the last one in a message.
*
* @param type The type of the record.
* @param payload The payload of the record.
* @param id The id associated with the record.
* @param chunk If true then this record is a chunk of a bigger record.
* @param last_record If true then this record is the last of the message
* containing it.
*/
Record(
RecordType type,
const RecordPayload &payload,
const RecordID &id,
bool chunk,
bool last_record
) :
type(type),
payload(payload),
id(id),
chunk(chunk),
last_record(last_record)
{ }
/**
* Type of the record.
*/
RecordType type;
/**
* Value of the payload.
*/
RecordPayload payload;
/**
* ID of the record.
*/
RecordID id;
/**
* If true, this record is a chunked record.
*/
bool chunk:1;
/**
* If true, this record is the last one of the payload containing it.
*/
bool last_record:1;
};
/**
* @}
*/
} // namespace ndef
} // namespace nfc
} // namespace mbed
#endif /* NFC_NDEF_RECORD_H_ */

View File

@ -0,0 +1,168 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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.
*/
#ifndef NFC_NDEF_RECORDPARSER_H_
#define NFC_NDEF_RECORDPARSER_H_
#include <stddef.h>
#include "Record.h"
namespace mbed {
namespace nfc {
namespace ndef {
/**
* @addtogroup nfc
* @{
*/
/**
* Parse a record.
*/
struct RecordParser {
/**
* Construct a record parser.
*/
RecordParser() : _next_parser(NULL) { }
/**
* Parse the record in input.
* @param record The NDEF record to parse.
* @return true if decoding has succeeded and false otherwise.
*/
virtual bool parse(const Record &record) = 0;
protected:
/**
* Protected non virtual destructor.
* RecordParser subclasses are not meant to be destroyed as RecordParser's.
*/
~RecordParser() { }
private:
friend class RecordParserChain;
RecordParser *_next_parser;
};
/**
* GenericRecordParser.
*
* @tparam ParserImplementation the implementation type of the parser.
* It must provides A decoding function named do_parse that accept a const
* reference to a record and a reference to the type parsed and return a boolean
* status that indicates the result of the parsing operation.
*
* @tparam ParsingResult Type produced by the parsing operation when successful.
*/
template<typename ParserImplementation, typename ParsingResult>
struct GenericRecordParser : public RecordParser {
/**
* Handle that receives parsed values.
*/
struct Delegate {
/**
* Called when a record has been parsed and converted into a value_type.
* @param record The record in its parsed form.
*/
virtual void on_record_parsed(const ParsingResult &record, const RecordID &id) = 0;
protected:
~Delegate() { }
};
/**
* Construct a record parser.
*/
GenericRecordParser() : _delegate(NULL) { }
/**
* @see RecordParser::parse
*/
virtual bool parse(const Record &record)
{
ParsingResult parsed_value;
if (static_cast<ParserImplementation*>(this)->do_parse(record, parsed_value)) {
if (_delegate) {
_delegate->on_record_parsed(parsed_value, record.id);
}
return true;
}
return false;
}
/**
* Set the handler that processes record parser.
* @param handler
*/
void set_delegate(Delegate *delegate)
{
_delegate = delegate;
}
protected:
/**
* Protected non virtual destructor.
*/
~GenericRecordParser() { }
private:
Delegate *_delegate;
};
/**
* Record parser chain.
*/
struct RecordParserChain {
/**
* Construct a parser chain.
*/
RecordParserChain() : _parsers(NULL) { }
/**
* Destroy a parser chain.
*/
~RecordParserChain() { }
/**
* Parse a record.
* @param record The record to parse.
* @return true if the record has been parsed and false otherwise.
*/
bool parse(const Record &record);
/**
* Add a parser at the end of the parser list.
* @param parser The parser to add.
*/
void set_next_parser(RecordParser *parser);
private:
RecordParser *_parsers;
};
/**
* @}
*/
} // namespace ndef
} // namespace nfc
} // namespace mbed
#endif /* NFC_NDEF_RECORDPARSER_H_ */

View File

@ -0,0 +1,153 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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 <cstring>
#include "Mime.h"
namespace mbed {
namespace nfc {
namespace common {
Mime::Mime() :
_mime(NULL),
_type_size(0),
_content_size(0)
{ }
Mime::Mime(
const Span<const uint8_t> &mime_type,
const Span<const uint8_t> &content
) : _mime(new uint8_t[mime_type.size() + content.size()]),
_type_size(mime_type.size()),
_content_size(content.size())
{
memcpy(_mime, mime_type.data(), mime_type.size());
memcpy(_mime + mime_type.size(), content.data(), content.size());
}
Mime::Mime(const Mime &to_copy) :
_mime(new uint8_t[to_copy.mime_size()]),
_type_size(to_copy._type_size),
_content_size(to_copy._content_size)
{
memcpy(_mime, to_copy._mime, to_copy.mime_size());
}
Mime::~Mime()
{
delete[] _mime;
}
Mime& Mime::operator=(const Mime &to_copy)
{
if (this == &to_copy) {
return * this;
}
delete[] _mime;
_mime = new uint8_t[to_copy.mime_size()];
memcpy(_mime, to_copy._mime, to_copy.mime_size());
_type_size = to_copy._type_size;
_content_size = to_copy._content_size;
return *this;
}
void Mime::set_mime(
const Span<const uint8_t> &mime_type,
const Span<const uint8_t> &content
) {
delete[] _mime;
_mime = new uint8_t[mime_type.size() + content.size()];
memcpy(_mime, mime_type.data(), mime_type.size());
memcpy(_mime + mime_type.size(), content.data(), content.size());
_type_size = mime_type.size();
_content_size = content.size();
}
Span<const uint8_t> Mime::get_mime_type() const
{
return make_const_Span(_mime, _type_size);
}
Span<const uint8_t> Mime::get_mime_content() const
{
return make_const_Span(_mime + _type_size, _content_size);
}
bool Mime::append_as_record(
ndef::MessageBuilder &message_builder,
bool is_last_record
) {
return message_builder.append_record(
ndef::RecordType(
ndef::RecordType::media_type,
get_mime_type()
),
get_mime_content(),
is_last_record
);
}
void Mime::move_data(
uint8_t *mime_record,
size_t mime_type_size,
size_t mime_content_size
) {
delete[] _mime;
_mime = mime_record;
_type_size = mime_type_size;
_content_size = mime_content_size;
}
size_t Mime::mime_size() const
{
return _type_size + _content_size;
}
bool MimeParser::do_parse(const ndef::Record &record, Mime &mime)
{
if (record.type.tnf != ndef::RecordType::media_type) {
return false;
}
// A type and a payload should be present
if (record.type.value.empty() || record.payload.empty()) {
return false;
}
// create the buffer
size_t type_size = record.type.value.size();
size_t content_size = record.payload.size();
uint8_t *mime_buffer = new uint8_t[type_size + content_size];
// copy type
memcpy(mime_buffer, record.type.value.data(), type_size);
// copy content
memcpy(mime_buffer + type_size, record.payload.data(), content_size);
mime.move_data(mime_buffer, type_size, content_size);
return true;
}
} // namespace common
} // namespace nfc
} // namespace mbed

View File

@ -0,0 +1,119 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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 "SimpleMessageParser.h"
namespace mbed {
namespace nfc {
namespace common {
SimpleMessageParser::SimpleMessageParser() :
_message_parser(),
_record_parser_chain(),
_uri_parser(),
_text_parser(),
_mime_parser(),
_delegate(NULL)
{
// setup the parser chain
_record_parser_chain.set_next_parser(&_uri_parser);
_record_parser_chain.set_next_parser(&_text_parser);
_record_parser_chain.set_next_parser(&_mime_parser);
// wire event handling
_message_parser.set_delegate(this);
_uri_parser.set_delegate(this);
_text_parser.set_delegate(this);
_mime_parser.set_delegate(this);
}
void SimpleMessageParser::set_delegate(Delegate *delegate)
{
_delegate = delegate;
}
void SimpleMessageParser::parse(const Span<const uint8_t> &data_buffer)
{
_message_parser.parse(data_buffer);
}
void SimpleMessageParser::add_record_parser(ndef::RecordParser *parser)
{
_record_parser_chain.set_next_parser(parser);
}
void SimpleMessageParser::on_parsing_error(ndef::MessageParser::error_t error)
{
if (_delegate) {
_delegate->on_parsing_error(error);
}
}
void SimpleMessageParser::on_parsing_started()
{
if (_delegate) {
_delegate->on_parsing_started();
}
}
void SimpleMessageParser::on_record_parsed(const ndef::Record &record)
{
bool parsed = _record_parser_chain.parse(record);
if (!parsed && _delegate) {
_delegate->on_unknown_record_parsed(record);
}
}
void SimpleMessageParser::on_parsing_terminated()
{
if (_delegate) {
_delegate->on_parsing_terminated();
}
}
void SimpleMessageParser::on_record_parsed(
const URI &uri,
const ndef::RecordID &id
) {
if (_delegate) {
_delegate->on_uri_parsed(uri, id);
}
}
void SimpleMessageParser::on_record_parsed(
const Text &text,
const ndef::RecordID &id
) {
if (_delegate) {
_delegate->on_text_parsed(text, id);
}
}
void SimpleMessageParser::on_record_parsed(
const Mime &mime,
const ndef::RecordID &id
) {
if (_delegate) {
_delegate->on_mime_parsed(mime, id);
}
}
} // namespace common
} // namespace nfc
} // namespace mbed

View File

@ -0,0 +1,171 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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 <cstring>
#include "Text.h"
namespace {
static const uint8_t utf16_encoding_bit = (1 << 7);
static const uint8_t language_code_size_mask = 0x3F;
static const uint8_t header_index = 0;
static const uint8_t language_code_index = 1;
static const uint8_t header_size = 1;
static const uint8_t text_record_type_value[] = { 'T' };
}
namespace mbed {
namespace nfc {
namespace common {
Text::Text() :
_text_record(NULL),
_text_record_size(0)
{ }
Text::Text(const Text &other) :
_text_record(other._text_record ? new uint8_t[other._text_record_size] : NULL),
_text_record_size(other._text_record_size)
{
memcpy(_text_record, other._text_record, _text_record_size);
}
Text::Text(
encoding_t text_encoding,
const Span<const uint8_t> &language_code,
const Span<const uint8_t> &text
) : _text_record(NULL),
_text_record_size(0)
{
set_text(text_encoding, language_code, text);
}
Text::~Text()
{
delete[] _text_record;
}
Text& Text::operator=(const Text &other)
{
if (this == &other) {
return *this;
}
_text_record_size = other._text_record_size;
delete[] _text_record;
if (!other._text_record) {
_text_record = NULL;
} else {
_text_record = new uint8_t[_text_record_size];
memcpy(_text_record, other._text_record, _text_record_size);
}
return *this;
}
void Text::set_text(
encoding_t text_encoding,
const Span<const uint8_t> &language_code,
const Span<const uint8_t> &text
) {
delete[] _text_record;
_text_record_size = header_size + language_code.size() + text.size();
_text_record = new uint8_t[_text_record_size];
// build the header
_text_record[header_index] = 0;
if (text_encoding == UTF16) {
_text_record[header_index] |= utf16_encoding_bit;
}
_text_record[header_index] |= language_code.size();
// language code
memcpy(_text_record + language_code_index, language_code.data(), language_code.size());
// actual text
memcpy(_text_record + language_code_index + language_code.size(), text.data(), text.size());
}
Text::encoding_t Text::get_encoding() const
{
return (_text_record[header_index] & utf16_encoding_bit) ? UTF16 : UTF8;
}
Span<const uint8_t> Text::get_language_code() const {
return make_const_Span(
_text_record + language_code_index,
_text_record[header_index] & language_code_size_mask
);
}
Span<const uint8_t> Text::get_text() const {
size_t language_code_size = get_language_code().size();
return make_const_Span(
_text_record + header_size + language_code_size,
_text_record_size - header_size - language_code_size
);
}
void Text::move_data(uint8_t *text, size_t size) {
delete[] _text_record;
_text_record = text;
_text_record_size = size;
}
bool Text::append_as_record(
ndef::MessageBuilder &message_builder,
bool is_last_record
) {
// Build the record type
ndef::RecordType type(
ndef::RecordType::well_known_type,
text_record_type_value
);
// build the record payload
ndef::RecordPayload payload(_text_record, _text_record_size);
return message_builder.append_record(type, payload, is_last_record);
}
bool TextParser::do_parse(const ndef::Record &record, Text &text)
{
if (record.type.tnf != ndef::RecordType::well_known_type) {
return false;
}
// the record type value should be equal to `T`
if (record.type.value != make_const_Span(text_record_type_value) ||
record.payload.empty()
) {
return false;
}
// create the buffer
size_t text_record_size = record.payload.size();
uint8_t *text_record = new uint8_t[text_record_size];
memcpy(text_record, record.payload.data(), text_record_size);
text.move_data(text_record, text_record_size);
return true;
}
} // namespace common
} // namespace nfc
} // namespace mbed

View File

@ -0,0 +1,162 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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 <cstring>
#include "URI.h"
namespace {
static const uint8_t uri_id_code_size = 1;
static const uint8_t uri_id_index = 0;
static const uint8_t uri_field_index = 1;
static const uint8_t uri_record_type_value[] = { 'U' } ;
}
namespace mbed {
namespace nfc {
namespace common {
URI::URI() :
_uri(NULL),
_uri_size(0) {
}
URI::URI(uri_identifier_code_t id, const Span<const uint8_t> &uri_field) :
_uri(uri_field.size() ? new uint8_t[uri_id_code_size + uri_field.size()] : NULL),
_uri_size(uri_id_code_size + uri_field.size())
{
_uri[uri_id_index] = id;
memcpy(_uri + uri_field_index, uri_field.data(), uri_field.size());
}
URI::URI(const URI &other) :
_uri(other._uri ? new uint8_t[other._uri_size] : NULL),
_uri_size(other._uri_size)
{
memcpy(_uri, other._uri, other._uri_size);
}
URI::~URI()
{
delete[] _uri;
}
URI& URI::operator=(const URI &other)
{
delete[] _uri;
if (!other._uri) {
_uri = NULL;
_uri_size = 0;
} else {
_uri = new uint8_t[other._uri_size];
_uri_size = other._uri_size;
memcpy(_uri, other._uri, other._uri_size);
}
return *this;
}
void URI::set_uri(
uri_identifier_code_t id,
const Span<const uint8_t> &uri_field
) {
delete[] _uri;
if (uri_field.empty()) {
_uri = NULL;
_uri_size = 0;
return;
}
_uri = new uint8_t[uri_id_code_size + uri_field.size()];
_uri_size = uri_id_code_size + uri_field.size();
_uri[uri_id_index] = id;
memcpy(_uri + uri_field_index, uri_field.data(), uri_field.size());
}
URI::uri_identifier_code_t URI::get_id() const
{
if (!_uri) {
return NA;
}
return static_cast<uri_identifier_code_t>(_uri[uri_id_index]);
}
Span<const uint8_t> URI::get_uri_field() const
{
if (!_uri) {
return Span<const uint8_t>();
}
return make_const_Span(
_uri + uri_field_index,
_uri_size - uri_id_code_size
);
}
bool URI::append_as_record(ndef::MessageBuilder &message_builder, bool is_last_record)
{
if (!_uri) {
return false;
}
// Build the record type
ndef::RecordType type(
ndef::RecordType::well_known_type,
uri_record_type_value
);
// build the record payload
ndef::RecordPayload payload(_uri, _uri_size);
return message_builder.append_record(type, payload, is_last_record);
}
void URI::move_data(uint8_t *new_uri, size_t new_uri_size)
{
delete[] _uri;
_uri = new_uri;
_uri_size = new_uri_size;
}
bool URIParser::do_parse(const ndef::Record &record, URI &uri)
{
if (record.type.tnf != ndef::RecordType::well_known_type) {
return false;
}
// the record type value should be equal to `U`
if (record.type.value != make_const_Span(uri_record_type_value) ||
record.payload.empty()
) {
return false;
}
// create the buffer
size_t uri_record_size = record.payload.size();
uint8_t *uri_record = new uint8_t[uri_record_size];
memcpy(uri_record, record.payload.data(), uri_record_size);
uri.move_data(uri_record, uri_record_size);
return true;
}
} // namespace common
} // namespace nfc
} // namespace mbed

View File

@ -0,0 +1,296 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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 "MessageBuilder.h"
#include <cstring>
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 Record &record)
{
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 (!record.payload.empty()) {
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);
if (record_size > (_message_buffer.size() - _position)) {
return false;
}
append_header(record);
append_type_length(record);
append_payload_length(record);
append_id_length(record);
append_type(record);
append_id(record);
append_payload(record);
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)
{
size_t record_size = 0;
record_size = 1; /* header */
record_size += 1; /* type length */
record_size += is_short_payload(record) ? 1 : 4;
if (!record.id.empty()) {
record_size += 1;
}
record_size += record.type.value.size();
record_size += record.id.size();
record_size += record.payload.size();
return record_size;
}
void MessageBuilder::append_header(const Record &record)
{
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)) {
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)
{
if (record.payload.empty()) {
_message_buffer[_position++] = 0;
return;
}
if (is_short_payload(record)) {
_message_buffer[_position++] = record.payload.size();
} else {
// TODO: proper host to network
uint32_t size = record.payload.size();
_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)
{
if (record.payload.empty()) {
return;
}
memcpy(
_message_buffer.data() + _position,
record.payload.data(),
record.payload.size()
);
_position += record.payload.size();
}
bool MessageBuilder::is_short_payload(const Record &record)
{
if (record.payload.size() <= 255) {
return true;
} else {
return false;
}
}
} // namespace ndef
} // namespace nfc
} // namespace mbed

View File

@ -0,0 +1,313 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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 <cstring>
#include "MessageParser.h"
#include "Record.h"
namespace {
struct buffer_iterator_t {
buffer_iterator_t(const mbed::Span<const uint8_t> &buffer) :
buffer(buffer),
position(0)
{ }
uint8_t operator*()
{
return buffer[position];
}
buffer_iterator_t &operator++()
{
++position;
return *this;
}
buffer_iterator_t operator++(int)
{
buffer_iterator_t previous = *this;
++*this;
return previous;
}
buffer_iterator_t &operator+=(size_t increment)
{
position += increment;
return *this;
}
operator bool() const
{
return (position >= buffer.size()) ? false : true;
}
size_t remaining_size() const
{
return buffer.size() - position;
}
void read_le(uint8_t *dest, size_t size)
{
memcpy(dest, buffer.data() + position, size);
position += size;
}
void read_be(uint8_t *dest, size_t size)
{
// TODO: Needs proper network to host function
std::reverse_copy(
buffer.data() + position,
buffer.data() + position + size,
dest
);
position += size;
}
mbed::Span<const uint8_t> get_underlying_buffer() const
{
return buffer.last(buffer.size() - position);
}
private:
mbed::Span<const uint8_t> buffer;
mbed::Span<const uint8_t>::index_type position;
};
} // end of anonymous namespace
namespace mbed {
namespace nfc {
namespace ndef {
struct MessageParser::parsing_state_t {
parsing_state_t(const Span<const uint8_t> &data_buffer) :
it(data_buffer),
first_record_parsed(false),
last_record_parsed(false),
error(false)
{ }
buffer_iterator_t it;
bool first_record_parsed:1;
bool last_record_parsed:1;
bool error:1;
};
MessageParser::MessageParser() :
_delegate(NULL)
{ }
void MessageParser::set_delegate(Delegate *delegate)
{
_delegate = delegate;
}
void MessageParser::parse(const Span<const uint8_t> &data_buffer)
{
parsing_state_t parsing_state(data_buffer);
report_parsing_started();
while(parsing_state.it && parse_record(parsing_state));
if (!parsing_state.error && !parsing_state.last_record_parsed) {
report_parsing_error(MISSING_MESSAGE_END, parsing_state);
}
report_parsing_terminated();
}
bool MessageParser::parse_record(parsing_state_t &s)
{
if (s.error || s.last_record_parsed) {
return false;
}
// ensure that the header can be extracted
if (s.it.remaining_size() < 1) {
report_parsing_error(INSUFICIENT_DATA, s);
return false;
}
uint8_t header = *s.it++;
// NOTE: report an error until the chunk parsing design is sorted out
if (header & Header::chunk_flag_bit) {
report_parsing_error(CHUNK_RECORD_NOT_SUPPORTED, s);
return false;
}
// handle first record cases
if (s.first_record_parsed == false) {
if (header & Header::message_begin_bit) {
s.first_record_parsed = true;
} else {
report_parsing_error(INVALID_MESSAGE_START, s);
return false;
}
} else if (header & Header::message_begin_bit) {
report_parsing_error(INVALID_MESSAGE_START, s);
return false;
}
// handle last record
if (header & Header::message_end_bit) {
s.last_record_parsed = true;
}
// ensure their is enough space to contain the type length, payload
// length and id length
uint8_t lengths_size = compute_lengths_size(header);
if (s.it.remaining_size() < lengths_size) {
report_parsing_error(INSUFICIENT_DATA, s);
return false;
}
// extract the various length from the message
uint8_t type_length = extract_type_length(s);
uint32_t payload_length = extract_payload_length(s, header);
uint8_t id_length = extract_id_length(s, header);
// there should be enough bytes left in the buffer
if (s.it.remaining_size() < (type_length + id_length + payload_length)) {
report_parsing_error(INSUFICIENT_DATA, s);
return false;
}
// validate the Type Name Format of the header
switch (header & Header::tnf_bits) {
case RecordType::empty:
if (type_length || payload_length || id_length) {
report_parsing_error(INVALID_EMPTY_RECORD, s);
return false;
}
break;
case RecordType::well_known_type:
case RecordType::media_type:
case RecordType::absolute_uri:
case RecordType::external_type:
if (!type_length) {
report_parsing_error(MISSING_TYPE_VALUE, s);
return false;
}
break;
case RecordType::unknown:
if (type_length) {
report_parsing_error(INVALID_UNKNOWN_TYPE_LENGTH, s);
return false;
}
break;
case RecordType::unchanged:
// shouldn't be handled outside of chunk handling
report_parsing_error(INVALID_UNCHANGED_TYPE, s);
return false;
default:
report_parsing_error(INVALID_TYPE_NAME_FORMAT, s);
return false;
}
// build the record
Record record;
// flags
record.last_record = header & Header::message_end_bit;
// type
record.type.tnf = static_cast<RecordType::tnf_t>(header & Header::tnf_bits);
if (type_length) {
record.type.value = s.it.get_underlying_buffer().first(type_length);
s.it += type_length;
}
// id
if (id_length) {
record.id = s.it.get_underlying_buffer().first(id_length);
s.it += id_length;
}
// payload
if (payload_length) {
record.payload = s.it.get_underlying_buffer().first(payload_length);
s.it += payload_length;
}
s.it += payload_length;
report_record_parsed(record);
return true;
}
uint8_t MessageParser::compute_lengths_size(uint8_t header)
{
return 1 /* type_length size */ +
((header & Header::short_record_bit) ? 1 : 4) /* payload length */ +
((header & Header::id_length_bit) ? 1 : 0);
}
uint8_t MessageParser::extract_type_length(parsing_state_t &s)
{
return *s.it++;
}
uint32_t MessageParser::extract_payload_length(parsing_state_t &s, uint8_t header)
{
uint32_t payload_length = 0;
if (header & Header::short_record_bit) {
payload_length = *s.it++;
} else {
s.it.read_be(
reinterpret_cast<uint8_t*>(&payload_length),
sizeof(payload_length)
);
}
return payload_length;
}
uint8_t MessageParser::extract_id_length(parsing_state_t &s, uint8_t header)
{
return (header & Header::id_length_bit) ? *s.it++ : 0;
}
void MessageParser::report_parsing_started() {
if (_delegate) {
_delegate->on_parsing_started();
}
}
void MessageParser::report_record_parsed(const Record &record)
{
if (_delegate) {
_delegate->on_record_parsed(record);
}
}
void MessageParser::report_parsing_terminated()
{
if (_delegate) {
_delegate->on_parsing_terminated();
}
}
void MessageParser::report_parsing_error(error_t error, parsing_state_t &parsing_state)
{
parsing_state.error = true;
if (_delegate) {
_delegate->on_parsing_error(error);
}
}
} // namespace ndef
} // namespace nfc
} // namespace mbed

View File

@ -0,0 +1,50 @@
/* mbed Microcontroller Library
* Copyright (c) 2018 ARM Limited
*
* 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 "RecordParser.h"
namespace mbed {
namespace nfc {
namespace ndef {
bool RecordParserChain::parse(const Record &record)
{
RecordParser *current_parser = _parsers;
while (current_parser) {
if (current_parser->parse(record)) {
return true;
}
current_parser = current_parser->_next_parser;
}
return false;
}
void RecordParserChain::set_next_parser(RecordParser *parser)
{
if (!_parsers) {
_parsers = parser;
} else {
RecordParser *current_parser = _parsers;
while (current_parser->_next_parser) {
current_parser = current_parser->_next_parser;
}
current_parser->_next_parser = parser;
}
}
} // namespace ndef
} // namespace nfc
} // namespace mbed