KItinerary

berelement.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "berelement.h"
8
9#include <QDebug>
10#include <QIODevice>
11#include <QtEndian>
12
13using namespace KItinerary;
14
15enum {
16 BerLongTypeMask = 0x1F,
17 BerExtendedTypeMask = 0x80,
18 BerExtendedLenghtMask = 0x80,
19 BerVariableLengthMarker = 0x80,
20};
21
22BER::Element::Element() = default;
23
24BER::Element::Element(const QByteArray &data, int offset, int size)
25 : m_data(data)
26 , m_offset(offset)
27 , m_dataSize(size < 0 ? data.size() : std::min<int>(offset + size, data.size()))
28{
29 assert(m_dataSize <= m_data.size());
30 if (!isValid()) {
31 m_data.clear();
32 m_offset = -1;
33 m_dataSize = -1;
34 }
35}
36
37BER::Element::~Element() = default;
38
40{
41 if (m_offset < 0 || m_dataSize <= 0 || m_offset + 2 > m_dataSize) {
42 return false;
43 }
44
45 // check type size
46 const auto ts = typeSize();
47 if (ts < 0 || ts >= 4 || m_offset + ts + 1 > m_dataSize) {
48 return false;
49 }
50
51 // check size of length field
52 const auto ls = lengthSize();
53 if (ls <= 0 || ls >= 4 || m_offset + ts + ls > m_dataSize) {
54 return false;
55 }
56
57 // check size of the content
58 const auto cs = contentSize();
59 return cs >= 0 && m_offset + ts + ls + cs <= m_dataSize;
60}
61
62int BER::Element::typeSize() const
63{
64 assert(m_offset >= 0);
65 assert(m_offset + 1 < m_dataSize);
66 auto it = m_data.begin() + m_offset;
67 if (((*it) & BerLongTypeMask) != BerLongTypeMask) {
68 return 1;
69 }
70
71 while (it != m_data.end() && std::distance(m_data.begin(), it) < m_dataSize) {
72 ++it;
73 if (((*it) & BerExtendedTypeMask) == 0) {
74 return std::distance(m_data.begin(), it) - m_offset + 1;
75 }
76 }
77
78 return 0;
79}
80
81uint32_t BER::Element::type() const
82{
83 const auto ts = typeSize();
84
85 // this is actually complete wrong compared to BER encoding rules, but since pretty much all docs
86 // we works with use the encoded type ids rather than the decoded ones this is actually much more practical
87 // might be worth to eventually have two methods for this, one with proper decoding and one with the raw data
88 uint32_t result = 0;
89 for (int i = 0; i < ts; ++i) {
90 result <<= 8;
91 result += (uint8_t)*(m_data.constData() + m_offset + i);
92 }
93 return result;
94}
95
96int BER::Element::lengthSize() const
97{
98 const auto ts = typeSize();
99 const uint8_t firstLengthByte = *(m_data.constData() + m_offset + ts);
100 if (firstLengthByte == BerVariableLengthMarker) {
101 return 1;
102 }
103 if (firstLengthByte & BerExtendedLenghtMask) {
104 return (firstLengthByte & ~BerExtendedLenghtMask) + 1;
105 }
106 return 1;
107}
108
110{
111 const auto ts = typeSize();
112 const auto s = ts + lengthSize() + contentSize();
113 const uint8_t firstLengthByte = *(m_data.constData() + m_offset + ts);
114 if (firstLengthByte == BerVariableLengthMarker) {
115 return s + 2;
116 }
117 return s;
118}
119
120const char *BER::Element::rawData() const
121{
122 return m_data.constData() + m_offset;
123}
124
126{
127 const auto ts = typeSize();
128 const uint8_t firstLengthByte = *(m_data.constData() + m_offset + ts);
129 if (firstLengthByte == BerVariableLengthMarker) {
130 const auto idx = m_data.indexOf(QByteArray("\0\0", 2), m_offset + ts + 1);
131 if (idx + 1 > m_dataSize) {
132 return - 1;
133 }
134 return idx - m_offset - ts - 1;
135 }
136 if (firstLengthByte & BerExtendedLenghtMask) {
137 const auto ls = firstLengthByte & ~BerExtendedLenghtMask;
138 int result = 0;
139 for (int i = 0; i < ls; ++i) {
140 result <<= 8;
141 result += (uint8_t)*(m_data.constData() + m_offset + ts + 1 + i);
142 }
143 return result;
144 }
145 return firstLengthByte;
146}
147
148int BER::Element::contentOffset() const
149{
150 return m_offset + typeSize() + lengthSize();
151}
152
153const uint8_t* BER::Element::contentData() const
154{
155 return reinterpret_cast<const uint8_t*>(m_data.constData() + contentOffset());
156}
157
159{
160 return BER::Element(m_data, contentOffset(), contentSize());
161}
162
164{
165 const auto s = size();
166 if (m_dataSize <= m_offset + s) {
167 return {};
168 }
169 return BER::Element(m_data, m_offset + s, m_dataSize - m_offset - s);
170}
171
173{
174 auto e = first();
175 while (e.isValid()) {
176 if (e.type() == type) {
177 return e;
178 }
179 e = e.next();
180 }
181 return {};
182}
183
185{
186 const uint32_t beSize = qToBigEndian((uint32_t)(size));
187 constexpr auto maxBytes = sizeof(size);
188 static_assert(maxBytes == sizeof(beSize), "wrong size datatypes");
189
190 if (size <= 127) {
191 out->write(((const char*)&beSize) + maxBytes - 1, 1);
192 return;
193 }
194
195 uint32_t mask = 0xff000000;
196 static_assert(maxBytes == sizeof(mask), "wrong size datatypes");
197 for (int i = maxBytes; i > 0; --i) {
198 if (size & mask) {
199 uint8_t sizeTag = BerExtendedLenghtMask | i;
200 out->write((const char*)&sizeTag, 1);
201 out->write(((const char*)&beSize) + maxBytes - i, i);
202 return;
203 }
204 mask >>= 8;
205 }
206}
An element in BER/DER/X.690 encoding.
Definition berelement.h:28
int size() const
Size of the entire element (type, size and content).
int contentSize() const
Size of the value part of this element.
const uint8_t * contentData() const
Raw content data.
uint32_t type() const
Type, "right-aligned" in the returned 32bit value.
bool isValid() const
Returns true if this element has a valid structure and can be read from.
Element find(uint32_t type) const
Returns the first child element of the given type.
Element next() const
Next child element, for nested types.
const char * rawData() const
Raw data of this element.
static void writeSize(QIODevice *out, int size)
Writes the given size in BER encoding to out.
Element first() const
First child element, for nested types.
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
bool isValid(QStringView ifopt)
qint64 write(const QByteArray &data)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:48 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.