Skip to content

Commit 5bc893d

Browse files
authored
Improvements on lazy decoding of Asn1Codec. (#1880)
* Added const qualifier to data buffer parameter in decodeValue. * Changed GenericRecord to utilize smart pointer. - This change also fixes single value delete being used to delete an array. * Added Lazy class. * Added LazyEvaluationMixin for classes that need lazy evaluation. * Changed implementation of lazy decoding of Asn1Record. - The lazy decoding is implemented through the new LazyFieldEvaluationMixin class. - Marked most methods as `const` and the record value fields are marked as mutable to allow decode operation when the object is counted as const. - * Removed orignal Lazy implementation. Updated LazyMixin documentation. * Improved documentation. * Moved decodeValueIfNeeded to header as its a forward that can be inlined. * Merged decodeInternal in decode as it was just a forward to the method. * Method renames. * Lint * Removed ability to eagerly evaluate in base constructor due to dependency on virtual dispatch. * Added const to encode and toString. * Inlined decoding to Asn1Record for now. * Updated decode exception to std::runtime_error. * Added docstrings. * Updated enum names. * Reverted decoding to use encoded value as state. * Fix if condition. * Reverted decodeIfNeeded call in Asn1StringRecord. * Removed extra calls to decode value if needed in toStringList. The decode check is to be set as prerequesite in toString().
1 parent 52fad0f commit 5bc893d

File tree

2 files changed

+135
-102
lines changed

2 files changed

+135
-102
lines changed

Packet++/header/Asn1Codec.h

Lines changed: 78 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,20 @@ namespace pcpp
109109
NotApplicable = 255
110110
};
111111

112+
namespace internal
113+
{
114+
/// @enum Asn1LoadPolicy
115+
/// @brief Policy for when to evaluate (decode) ASN.1 record values.
116+
/// Determines whether the value is decoded immediately (eager) or on first access (lazy).
117+
enum class Asn1LoadPolicy
118+
{
119+
/// The value is evaluated on first access (lazy decoding).
120+
Lazy,
121+
/// The value is evaluated immediately on construction (eager decoding).
122+
Eager
123+
};
124+
} // namespace internal
125+
112126
/// @class Asn1Record
113127
/// Represents an ASN.1 record, as described in ITU-T Recommendation X.680:
114128
/// <https://www.itu.int/rec/T-REC-X.680/en>
@@ -127,7 +141,7 @@ namespace pcpp
127141

128142
/// Encode this record and convert it to a byte stream
129143
/// @return A vector of bytes representing the record
130-
std::vector<uint8_t> encode();
144+
std::vector<uint8_t> encode() const;
131145

132146
/// @return The ASN.1 tag class
133147
Asn1TagClass getTagClass() const
@@ -164,7 +178,7 @@ namespace pcpp
164178
}
165179

166180
/// @return A string representation of the record
167-
std::string toString();
181+
std::string toString() const;
168182

169183
/// A templated method that accepts a class derived from Asn1Record as its template argument and attempts
170184
/// to cast the current instance to that type
@@ -190,26 +204,41 @@ namespace pcpp
190204
size_t m_ValueLength = 0;
191205
size_t m_TotalLength = 0;
192206

193-
uint8_t const* m_EncodedValue = nullptr;
194-
195207
Asn1Record() = default;
196208

197-
static std::unique_ptr<Asn1Record> decodeInternal(const uint8_t* data, size_t dataLen, bool lazy);
209+
/// @brief Decodes the record value from a byte array into the mutable cache variables.
210+
/// This method is marked as const as it can be called on a const instance of the record for lazy decoding.
211+
virtual void decodeValue(uint8_t const* data) const = 0;
198212

199-
virtual void decodeValue(uint8_t const* data, bool lazy) = 0;
213+
/// @brief Encodes the record value into a byte array
214+
/// Prefer using encodeValueSafe() to ensure the value is decoded first if needed
200215
virtual std::vector<uint8_t> encodeValue() const = 0;
201216

217+
/// @brief Encodes the record value into a byte array, ensuring that the value is decoded first if needed
218+
std::vector<uint8_t> encodeValueSafe() const
219+
{
220+
decodeValueIfNeeded();
221+
return encodeValue();
222+
}
223+
202224
static std::unique_ptr<Asn1Record> decodeTagAndCreateRecord(const uint8_t* data, size_t dataLen,
203225
uint8_t& tagLen);
204226
uint8_t decodeLength(const uint8_t* data, size_t dataLen);
205-
void decodeValueIfNeeded();
227+
void decodeValueIfNeeded() const;
206228

207-
uint8_t encodeTag();
229+
uint8_t encodeTag() const;
208230
std::vector<uint8_t> encodeLength() const;
209231

210-
virtual std::vector<std::string> toStringList();
232+
// note: Requires the value to be decoded first if lazy decoding is used
233+
virtual std::vector<std::string> toStringList() const;
211234

212235
friend class Asn1ConstructedRecord;
236+
237+
private:
238+
void setEncodedValue(uint8_t const* dataSource,
239+
internal::Asn1LoadPolicy loadPolicy = internal::Asn1LoadPolicy::Lazy);
240+
241+
mutable uint8_t const* m_EncodedValue = nullptr;
213242
};
214243

215244
/// @class Asn1GenericRecord
@@ -248,11 +277,11 @@ namespace pcpp
248277
protected:
249278
Asn1GenericRecord() = default;
250279

251-
void decodeValue(uint8_t const* data, bool lazy) override;
280+
void decodeValue(uint8_t const* data) const override;
252281
std::vector<uint8_t> encodeValue() const override;
253282

254283
private:
255-
std::unique_ptr<uint8_t[]> m_Value = nullptr;
284+
mutable std::unique_ptr<uint8_t[]> m_Value = nullptr;
256285

257286
void init(Asn1TagClass tagClass, bool isConstructed, uint8_t tagType, const uint8_t* value, size_t valueLen);
258287
};
@@ -289,10 +318,10 @@ namespace pcpp
289318
protected:
290319
Asn1ConstructedRecord() = default;
291320

292-
void decodeValue(uint8_t const* data, bool lazy) override;
321+
void decodeValue(uint8_t const* data) const override;
293322
std::vector<uint8_t> encodeValue() const override;
294323

295-
std::vector<std::string> toStringList() override;
324+
std::vector<std::string> toStringList() const override;
296325

297326
template <typename Iterator> void init(Asn1TagClass tagClass, uint8_t tagType, Iterator begin, Iterator end)
298327
{
@@ -304,7 +333,7 @@ namespace pcpp
304333
for (Iterator recordIter = begin; recordIter != end; ++recordIter)
305334
{
306335
auto encodedRecord = (*recordIter)->encode();
307-
auto copyRecord = Asn1Record::decode(encodedRecord.data(), encodedRecord.size(), false);
336+
auto copyRecord = Asn1Record::decode(encodedRecord.data(), encodedRecord.size(), LazySubRecordDecoding);
308337
m_SubRecords.pushBack(std::move(copyRecord));
309338
recordValueLength += encodedRecord.size();
310339
}
@@ -314,7 +343,10 @@ namespace pcpp
314343
}
315344

316345
private:
317-
PointerVector<Asn1Record> m_SubRecords;
346+
// Set to false as there are issues with lazy decoding of sub-records in some cases.
347+
static constexpr bool LazySubRecordDecoding = false;
348+
349+
mutable PointerVector<Asn1Record> m_SubRecords;
318350
};
319351

320352
/// @class Asn1SequenceRecord
@@ -389,7 +421,7 @@ namespace pcpp
389421

390422
/// @return The integer value of this record
391423
/// @throw std::invalid_argument if the value doesn't fit the requested integer size
392-
template <typename T, EnableIfUnsignedIntegral<T> = 0> T getIntValue()
424+
template <typename T, EnableIfUnsignedIntegral<T> = 0> T getIntValue() const
393425
{
394426
decodeValueIfNeeded();
395427
return m_Value.getInt<T>();
@@ -403,7 +435,7 @@ namespace pcpp
403435
}
404436

405437
/// @return A hex string representation of the record value
406-
std::string getValueAsString()
438+
std::string getValueAsString() const
407439
{
408440
decodeValueIfNeeded();
409441
return m_Value.toString();
@@ -412,10 +444,10 @@ namespace pcpp
412444
protected:
413445
Asn1IntegerRecord() = default;
414446

415-
void decodeValue(uint8_t const* data, bool lazy) override;
447+
void decodeValue(uint8_t const* data) const override;
416448
std::vector<uint8_t> encodeValue() const override;
417449

418-
std::vector<std::string> toStringList() override;
450+
std::vector<std::string> toStringList() const override;
419451

420452
private:
421453
class BigInt
@@ -475,7 +507,7 @@ namespace pcpp
475507
}
476508
};
477509

478-
BigInt m_Value;
510+
mutable BigInt m_Value;
479511
};
480512

481513
/// @class Asn1EnumeratedRecord
@@ -500,7 +532,7 @@ namespace pcpp
500532
{
501533
public:
502534
/// @return The string value of this record
503-
std::string getValue()
535+
std::string getValue() const
504536
{
505537
decodeValueIfNeeded();
506538
return m_Value;
@@ -516,7 +548,7 @@ namespace pcpp
516548
m_TotalLength = m_ValueLength + 2;
517549
}
518550

519-
void decodeValue(uint8_t const* data, bool lazy) override
551+
void decodeValue(uint8_t const* data) const override
520552
{
521553
m_Value = std::string(reinterpret_cast<char const*>(data), m_ValueLength);
522554
}
@@ -525,12 +557,12 @@ namespace pcpp
525557
return { m_Value.begin(), m_Value.end() };
526558
}
527559

528-
std::vector<std::string> toStringList() override
560+
std::vector<std::string> toStringList() const override
529561
{
530562
return { Asn1Record::toStringList().front() + ", Value: " + getValue() };
531563
}
532564

533-
std::string m_Value;
565+
mutable std::string m_Value;
534566
};
535567

536568
/// @class Asn1OctetStringRecord
@@ -553,13 +585,13 @@ namespace pcpp
553585
{}
554586

555587
protected:
556-
void decodeValue(uint8_t const* data, bool lazy) override;
588+
void decodeValue(uint8_t const* data) const override;
557589
std::vector<uint8_t> encodeValue() const override;
558590

559591
private:
560592
Asn1OctetStringRecord() = default;
561593

562-
bool m_IsPrintable = true;
594+
mutable bool m_IsPrintable = true;
563595
};
564596

565597
/// @class Asn1UTF8StringRecord
@@ -622,22 +654,22 @@ namespace pcpp
622654
explicit Asn1BooleanRecord(bool value);
623655

624656
/// @return The boolean value of this record
625-
bool getValue()
657+
bool getValue() const
626658
{
627659
decodeValueIfNeeded();
628660
return m_Value;
629661
};
630662

631663
protected:
632-
void decodeValue(uint8_t const* data, bool lazy) override;
664+
void decodeValue(uint8_t const* data) const override;
633665
std::vector<uint8_t> encodeValue() const override;
634666

635-
std::vector<std::string> toStringList() override;
667+
std::vector<std::string> toStringList() const override;
636668

637669
private:
638670
Asn1BooleanRecord() = default;
639671

640-
bool m_Value = false;
672+
mutable bool m_Value = false;
641673
};
642674

643675
/// @class Asn1NullRecord
@@ -651,7 +683,7 @@ namespace pcpp
651683
Asn1NullRecord();
652684

653685
protected:
654-
void decodeValue(uint8_t const* data, bool lazy) override
686+
void decodeValue(uint8_t const* data) const override
655687
{}
656688
std::vector<uint8_t> encodeValue() const override
657689
{
@@ -728,20 +760,20 @@ namespace pcpp
728760
explicit Asn1ObjectIdentifierRecord(const Asn1ObjectIdentifier& value);
729761

730762
/// @return The OID value of this record
731-
const Asn1ObjectIdentifier& getValue()
763+
const Asn1ObjectIdentifier& getValue() const
732764
{
733765
decodeValueIfNeeded();
734766
return m_Value;
735767
}
736768

737769
protected:
738-
void decodeValue(uint8_t const* data, bool lazy) override;
770+
void decodeValue(uint8_t const* data) const override;
739771
std::vector<uint8_t> encodeValue() const override;
740772

741-
std::vector<std::string> toStringList() override;
773+
std::vector<std::string> toStringList() const override;
742774

743775
private:
744-
Asn1ObjectIdentifier m_Value;
776+
mutable Asn1ObjectIdentifier m_Value;
745777

746778
Asn1ObjectIdentifierRecord() = default;
747779
};
@@ -756,7 +788,7 @@ namespace pcpp
756788
/// timezones. The default value is UTC
757789
/// @return The time-point value of this record
758790
/// @throws std::invalid_argument if timezone is not in the correct format
759-
std::chrono::system_clock::time_point getValue(const std::string& timezone = "Z")
791+
std::chrono::system_clock::time_point getValue(const std::string& timezone = "Z") const
760792
{
761793
decodeValueIfNeeded();
762794
return adjustTimezones(m_Value, "Z", timezone);
@@ -769,16 +801,16 @@ namespace pcpp
769801
/// @return The value as string
770802
/// @throws std::invalid_argument if timezone is not in the correct format
771803
std::string getValueAsString(const std::string& format = "%Y-%m-%d %H:%M:%S", const std::string& timezone = "Z",
772-
bool includeMilliseconds = false);
804+
bool includeMilliseconds = false) const;
773805

774806
protected:
775807
Asn1TimeRecord() = default;
776808
explicit Asn1TimeRecord(Asn1UniversalTagType tagType, const std::chrono::system_clock::time_point& value,
777809
const std::string& timezone);
778810

779-
std::chrono::system_clock::time_point m_Value;
811+
mutable std::chrono::system_clock::time_point m_Value;
780812

781-
std::vector<std::string> toStringList() override;
813+
std::vector<std::string> toStringList() const override;
782814

783815
static void validateTimezone(const std::string& timezone);
784816
static std::chrono::system_clock::time_point adjustTimezones(const std::chrono::system_clock::time_point& value,
@@ -799,12 +831,12 @@ namespace pcpp
799831
explicit Asn1UtcTimeRecord(const std::chrono::system_clock::time_point& value, bool withSeconds = true);
800832

801833
protected:
802-
void decodeValue(uint8_t const* data, bool lazy) override;
834+
void decodeValue(uint8_t const* data) const override;
803835
std::vector<uint8_t> encodeValue() const override;
804836

805837
private:
806838
Asn1UtcTimeRecord() = default;
807-
bool m_WithSeconds = true;
839+
mutable bool m_WithSeconds = true;
808840
};
809841

810842
/// @class Asn1GeneralizedTimeRecord
@@ -823,12 +855,12 @@ namespace pcpp
823855
const std::string& timezone = "Z");
824856

825857
protected:
826-
void decodeValue(uint8_t const* data, bool lazy) override;
858+
void decodeValue(uint8_t const* data) const override;
827859
std::vector<uint8_t> encodeValue() const override;
828860

829861
private:
830862
Asn1GeneralizedTimeRecord() = default;
831-
std::string m_Timezone;
863+
mutable std::string m_Timezone;
832864
};
833865

834866
/// @class Asn1BitStringRecord
@@ -858,10 +890,10 @@ namespace pcpp
858890
}
859891

860892
protected:
861-
void decodeValue(uint8_t const* data, bool lazy) override;
893+
void decodeValue(uint8_t const* data) const override;
862894
std::vector<uint8_t> encodeValue() const override;
863895

864-
std::vector<std::string> toStringList() override;
896+
std::vector<std::string> toStringList() const override;
865897

866898
private:
867899
class BitSet
@@ -890,6 +922,6 @@ namespace pcpp
890922

891923
Asn1BitStringRecord() = default;
892924

893-
BitSet m_Value;
925+
mutable BitSet m_Value;
894926
};
895927
} // namespace pcpp

0 commit comments

Comments
 (0)