/****************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtKnx module. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ******************************************************************************/ #ifndef QKNXNETIPSTRUCTHEADER_H #define QKNXNETIPSTRUCTHEADER_H #include #include #include #include QT_BEGIN_NAMESPACE template class QKnxNetIpStructHeader final { static_assert(is_type::value, "Type not supported."); public: QKnxNetIpStructHeader() = default; ~QKnxNetIpStructHeader() = default; explicit QKnxNetIpStructHeader(CodeType c) : QKnxNetIpStructHeader(c, 0) {} QKnxNetIpStructHeader(CodeType c, quint16 dataFieldSize) { setCode(c); setDataSize(dataFieldSize); } template ::value>::type> QKnxNetIpStructHeader(CodeType c, quint16 dSize, bool mandatory) { setCode(c); setDataSize(dSize); setMandatory(mandatory); } bool isNull() const { return bool(size() == 0); } bool isValid() const { if (isNull()) return false; return QKnxNetIp::isStructType(code()); } quint8 size() const { return m_bytes[0]; } quint16 totalSize() const { if (size() == 2) return byte(0); if (size() == 4) return quint16(quint16(byte(1)) << 8 | byte(2)); return 0; } quint16 dataSize() const { return totalSize() - size(); } void setDataSize(quint16 dataFieldSize) { // 2.1.3 Structures: If the amount of data exceeds 252 octets, the length octet shall // be 0xff and the next two octets shall contain the length as a 16 bit value. Then the // structure data shall start at the fifth octet. const auto tmp = code(); setSize(dataFieldSize > 0xfc ? 4 : 2); if (dataFieldSize > 0xfc) { setByte(0, 0xff); setByte(1, quint8((dataFieldSize + 4) >> 8)); setByte(2, quint8(dataFieldSize + 4)); } else { setByte(0, quint8(dataFieldSize + 2)); } setCode(tmp); } CodeType code() const { return codeType(); } void setCode(CodeType c) { setCodeType(c); } quint8 byte(quint8 index) const { Q_ASSERT_X(index < size(), "QKnxNetIpStructHeader::byte", "index out of range"); return m_bytes[index + 1]; } QKnxByteArray bytes() const { if (!isNull()) { if (size() == 2) return { byte(0), byte(1) }; if (size() == 4) return { byte(0), byte(1), byte(2), byte(3) }; } return {}; } static QKnxNetIpStructHeader fromBytes(const QKnxByteArray &bytes, quint16 index = 0) { const qint32 availableSize = bytes.size() - index; if (availableSize < 1) return {}; // total size missing quint16 totalSize = QKnxUtils::QUint8::fromBytes(bytes, index); const quint8 headerSize = totalSize == 0xff ? 4 : 2; if (availableSize < headerSize) return {}; // total size and code missing if (headerSize == 4) totalSize = QKnxUtils::QUint16::fromBytes(bytes, index + 1); auto code = CodeType(QKnxUtils::QUint8::fromBytes(bytes, index + headerSize - 1)); if (!QKnxNetIp::isStructType(code)) return {}; return createHeader(code, quint16(totalSize - headerSize)); } bool operator==(const QKnxNetIpStructHeader &other) const { if (sizeof(m_bytes) != sizeof(other.m_bytes)) return false; return memcmp(m_bytes, other.m_bytes, sizeof(m_bytes)) == 0; } bool operator!=(const QKnxNetIpStructHeader &other) const { return !operator==(other); } template typename std::enable_if::value, bool>::type /* bool */ isMandatory() const { if (isNull()) return false; return (byte(size() - 1) & 0x80); } template typename std::enable_if::value, void>::type /* void */ setMandatory(bool value) { if (isNull()) { setSize(2); setByte(0, 2); } setByte(size() - 1, ((byte(size() - 1)) & ~(1 << 7)) | (quint8(value) << 7)); } private: void setSize(quint8 structSize) { m_bytes[0] = structSize; } void setByte(quint8 index, quint8 value) { m_bytes[index + 1] = value; } template typename std::enable_if::value, CodeType>::type /* CodeType */ codeType() const { if (isNull()) return CodeType::Unknown; return CodeType(byte(size() - 1) &~ 0x80); } template typename std::enable_if::value, CodeType>::type /* CodeType */ codeType() const { if (isNull()) return CodeType::Unknown; return CodeType(byte(size() - 1)); } template typename std::enable_if::value, void>::type /* void */ setCodeType(CodeType c) { if (isNull()) { setSize(2); setByte(0, 2); } setByte(size() - 1, (quint8(c) & ~(1 << 7)) | (quint8(isMandatory()) << 7)); } template typename std::enable_if::value, void>::type /* void */ setCodeType(CodeType c) { if (isNull()) { setSize(2); setByte(0, 2); } setByte(size() - 1, quint8(c)); } template static typename std::enable_if::value, QKnxNetIpStructHeader>::type createHeader(CodeType code, quint16 dataSize) { QKnxNetIpStructHeader result { code, dataSize }; result.setMandatory((quint8(code) & 0x80) != 0); return result; } template static typename std::enable_if::value, QKnxNetIpStructHeader>::type createHeader(CodeType code, quint16 dataSize) { return { code, dataSize }; } private: // first byte denotes size, not part of the header quint8 m_bytes[5] { 0x00, 0x00, 0x00, 0x00, 0x00 }; }; template inline QDebug operator<<(QDebug debug, const QKnxNetIpStructHeader &header) { QDebugStateSaver _(debug); return debug.nospace().noquote() << "0x" << header.bytes().toHex(); } QT_END_NAMESPACE #endif