@@ -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