/************************************************************************************** * Copyright (c) 2016, Tomoaki Yamaguchi * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation * Tieto Poland Sp. z o.o. - Gateway improvements **************************************************************************************/ #include "MQTTSNGWTopic.h" #include "MQTTSNGWDefines.h" #include "MQTTSNGateway.h" #include using namespace MQTTSNGW; /*===================================== Class Topic ======================================*/ Topic::Topic() { _type = MQTTSN_TOPIC_TYPE_NORMAL; _topicName = nullptr; _topicId = 0; _next = nullptr; } Topic::Topic(string* topic, MQTTSN_topicTypes type) { _type = type; _topicName = topic; _topicId = 0; _next = nullptr; } Topic::~Topic() { if ( _topicName ) { delete _topicName; } } string* Topic::getTopicName(void) { return _topicName; } uint16_t Topic::getTopicId(void) { return _topicId; } MQTTSN_topicTypes Topic::getType(void) { return _type; } bool Topic::isMatch(string* topicName) { string::size_type tlen = _topicName->size(); string::size_type tpos = 0; string::size_type tloc = 0; string::size_type pos = 0; string::size_type loc = 0; string wildcard = "#"; string wildcards = "+"; while(true) { loc = topicName->find('/', pos); tloc = _topicName->find('/', tpos); if ( loc != string::npos && tloc != string::npos ) { string subtopic = topicName->substr(pos, loc - pos); string subtopict = _topicName->substr(tpos, tloc - tpos); if (subtopict == wildcard) { return true; } else if (subtopict == wildcards) { if ( (tpos = tloc + 1 ) > tlen ) { pos = loc + 1; loc = topicName->find('/', pos); if ( loc == string::npos ) { return true; } else { return false; } } pos = loc + 1; } else if ( subtopic != subtopict ) { return false; } else { if ( (tpos = tloc + 1) > tlen ) { return false; } pos = loc + 1; } } else if ( loc == string::npos && tloc == string::npos ) { string subtopic = topicName->substr(pos); string subtopict = _topicName->substr(tpos); if ( subtopict == wildcard || subtopict == wildcards) { return true; } else if ( subtopic == subtopict ) { return true; } else { return false; } } else if ( loc == string::npos && tloc != string::npos ) { string subtopic = topicName->substr(pos); string subtopict = _topicName->substr(tpos, tloc - tpos); if ( subtopic != subtopict) { return false; } tpos = tloc + 1; return _topicName->substr(tpos) == wildcard; } else if ( loc != string::npos && tloc == string::npos ) { return _topicName->substr(tpos) == wildcard; } } } void Topic::print(void) { WRITELOG("TopicName=%s ID=%d Type=%d\n", _topicName->c_str(), _topicId, _type); } /*===================================== Class Topics ======================================*/ Topics::Topics() { _first = nullptr; _nextTopicId = 0; _cnt = 0; } Topics::~Topics() { Topic* p = _first; while (p) { Topic* q = p->_next; delete p; p = q; } } Topic* Topics::getTopicByName(const MQTTSN_topicid* topicid) { Topic* p = _first; char* ch = topicid->data.long_.name; string sname = string(ch, ch + topicid->data.long_.len); while (p) { if ( p->_topicName->compare(sname) == 0 ) { return p; } p = p->_next; } return 0; } Topic* Topics::getTopicById(const MQTTSN_topicid* topicid) { Topic* p = _first; while (p) { if ( p->_type == topicid->type && p->_topicId == topicid->data.id ) { return p; } p = p->_next; } return 0; } // For MQTTSN_TOPIC_TYPE_NORMAL */ Topic* Topics::add(const MQTTSN_topicid* topicid) { if (topicid->type != MQTTSN_TOPIC_TYPE_NORMAL ) { return 0; } Topic* topic = getTopicByName(topicid); if ( topic ) { return topic; } string name(topicid->data.long_.name, topicid->data.long_.len); return add(name.c_str(), 0); } Topic* Topics::add(const char* topicName, uint16_t id) { MQTTSN_topicid topicId; if ( _cnt >= MAX_TOPIC_PAR_CLIENT ) { return 0; } topicId.data.long_.name = (char*)const_cast(topicName); topicId.data.long_.len = strlen(topicName); Topic* topic = getTopicByName(&topicId); if ( topic ) { return topic; } topic = new Topic(); if (topic == nullptr) { return nullptr; } string* name = new string(topicName); topic->_topicName = name; if ( id == 0 ) { topic->_type = MQTTSN_TOPIC_TYPE_NORMAL; topic->_topicId = getNextTopicId(); } else { topic->_type = MQTTSN_TOPIC_TYPE_PREDEFINED; topic->_topicId = id; } _cnt++; if ( _first == nullptr) { _first = topic; } else { Topic* tp = _first; while (tp) { if (tp->_next == nullptr) { tp->_next = topic; break; } else { tp = tp->_next; } } } return topic; } uint16_t Topics::getNextTopicId() { return ++_nextTopicId == 0xffff ? _nextTopicId += 2 : _nextTopicId; } Topic* Topics::match(const MQTTSN_topicid* topicid) { if (topicid->type != MQTTSN_TOPIC_TYPE_NORMAL) { return 0; } string topicName(topicid->data.long_.name, topicid->data.long_.len); Topic* topic = _first; while (topic) { if (topic->isMatch(&topicName)) { return topic; } topic = topic->_next; } return 0; } void Topics::eraseNormal(void) { Topic* topic = _first; Topic* next = nullptr; Topic* prev = nullptr; while (topic) { if ( topic->_type == MQTTSN_TOPIC_TYPE_NORMAL ) { next = topic->_next; if ( _first == topic ) { _first = next; } if ( prev ) { prev->_next = next; } delete topic; _cnt--; topic = next; } else { prev = topic; topic = topic->_next; } } } void Topics::print(void) { Topic* topic = _first; if (topic == nullptr ) { WRITELOG("No Topic.\n"); } else { while (topic) { topic->print(); topic = topic->_next; } } } uint8_t Topics::getCount(void) { return _cnt; } /*===================================== Class TopicIdMap =====================================*/ TopicIdMapElement::TopicIdMapElement(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) { _msgId = msgId; _topicId = topicId; _type = type; _next = nullptr; _prev = nullptr; } TopicIdMapElement::~TopicIdMapElement() { } MQTTSN_topicTypes TopicIdMapElement::getTopicType(void) { return _type; } uint16_t TopicIdMapElement::getTopicId(void) { return _topicId; } TopicIdMap::TopicIdMap() { _maxInflight = MAX_INFLIGHTMESSAGES; _msgIds = 0; _first = nullptr; _end = nullptr; _cnt = 0; } TopicIdMap::~TopicIdMap() { TopicIdMapElement* p = _first; while ( p ) { TopicIdMapElement* q = p->_next; delete p; p = q; } } TopicIdMapElement* TopicIdMap::getElement(uint16_t msgId) { TopicIdMapElement* p = _first; while ( p ) { if ( p->_msgId == msgId ) { return p; } p = p->_next; } return 0; } TopicIdMapElement* TopicIdMap::add(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) { if ( _cnt > _maxInflight * 2 || ( topicId == 0 && type != MQTTSN_TOPIC_TYPE_SHORT ) ) { return 0; } if ( getElement(msgId) > 0 ) { erase(msgId); } TopicIdMapElement* elm = new TopicIdMapElement(msgId, topicId, type); if ( elm == 0 ) { return 0; } if ( _first == nullptr ) { _first = elm; _end = elm; } else { elm->_prev = _end; _end->_next = elm; _end = elm; } _cnt++; return elm; } void TopicIdMap::erase(uint16_t msgId) { TopicIdMapElement* p = _first; while ( p ) { if ( p->_msgId == msgId ) { if ( p->_prev == nullptr ) { _first = p->_next; } else { p->_prev->_next = p->_next; } if ( p->_next == nullptr ) { _end = p->_prev; } else { p->_next->_prev = p->_prev; } delete p; break; } p = p->_next; } _cnt--; } void TopicIdMap::clear(void) { TopicIdMapElement* p = _first; while ( p ) { TopicIdMapElement* q = p->_next; delete p; p = q; } _first = nullptr; _end = nullptr; _cnt = 0; }