parse encapsulated structures (octet string or bit string containing valid ASN.1): improved memory management v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sat, 17 Jul 2021 18:13:27 +0200
branchv_0
changeset 32 00d76921c547
parent 31 a87c97aecbf6
child 33 13aa7280ac7b
parse encapsulated structures (octet string or bit string containing valid ASN.1): improved memory management
src/lib/ASN1Reader.h
src/lib/BasicASN1Reader.h
--- a/src/lib/ASN1Reader.h	Fri Jul 16 21:01:02 2021 +0200
+++ b/src/lib/ASN1Reader.h	Sat Jul 17 18:13:27 2021 +0200
@@ -31,11 +31,11 @@
 	virtual ~ASN1Reader() = default;
 
 	virtual void addHandler(std::shared_ptr<ASN1ContentHandler> handler) {
-		handlers.addHandler(handler);
+		handlers->addHandler(handler);
 	};
 
 protected:
-	ASN1ContentHandlerProxy handlers;
+	std::shared_ptr<ASN1ContentHandlerProxy> handlers = std::make_shared<ASN1ContentHandlerProxy>();
 };
 
 }
--- a/src/lib/BasicASN1Reader.h	Fri Jul 16 21:01:02 2021 +0200
+++ b/src/lib/BasicASN1Reader.h	Sat Jul 17 18:13:27 2021 +0200
@@ -57,7 +57,7 @@
 			LevelMetadata& l = level.back();
 			if (l.definiteLength && l.length == getBytesRead() - l.start) {
 				level.pop_back();
-				handlers.writeCollectionEnd();
+				handlers->writeCollectionEnd();
 				checkRemainingItems(); // multiple collections may end at the same point
 			}
 		}
@@ -120,7 +120,7 @@
 		// commit(); // TODO: commit here and recover later instead of rollback?
 
 		if (!started) {
-			handlers.writeStreamStart();
+			handlers->writeStreamStart();
 			started = true;
 		}
 
@@ -128,50 +128,50 @@
 
 		// TODO: constants, more types
 		if (typeHeader.tag == UniversalType::EndOfContent && typeHeader.tagClass == TagClass::Universal && typeHeader.pc == PC::Primitive) {
-			handlers.writeCollectionEnd();
+			handlers->writeCollectionEnd();
 		} else if (typeHeader.tag == UniversalType::Sequence) {
 			level.push_back({typeHeader.definiteLength, typeHeader.length, getBytesRead()}); // TODO: transaction
-			handlers.writeCollectionStart(typeHeader);
+			handlers->writeCollectionStart(typeHeader);
 		} else if (typeHeader.tag == UniversalType::Set) {
 			level.push_back({typeHeader.definiteLength, typeHeader.length, getBytesRead()}); // TODO: transaction
-			handlers.writeCollectionStart(typeHeader);
+			handlers->writeCollectionStart(typeHeader);
 		} else if (typeHeader.pc == PC::Constructed) {
 			level.push_back({typeHeader.definiteLength, typeHeader.length, getBytesRead()}); // TODO: transaction
-			handlers.writeCollectionStart(typeHeader);
+			handlers->writeCollectionStart(typeHeader);
 		} else if (typeHeader.tag == UniversalType::Null && typeHeader.length == 0) {
-			handlers.writeNull(typeHeader);
+			handlers->writeNull(typeHeader);
 		} else if (typeHeader.tag == UniversalType::Boolean && typeHeader.definiteLength && typeHeader.length == 1) {
 			bool value;
 			read((uint8_t*) & value, 1);
-			handlers.writeBoolean(typeHeader, value);
+			handlers->writeBoolean(typeHeader, value);
 		} else if (typeHeader.tag == UniversalType::Integer && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
 			// TODO: check available bytes before allocating buffer
 			std::vector<uint8_t> value(typeHeader.length, 0x00);
 			read(value.data(), typeHeader.length);
-			handlers.writeInteger(typeHeader, ASN1ContentHandler::Integer(value));
+			handlers->writeInteger(typeHeader, ASN1ContentHandler::Integer(value));
 		} else if (typeHeader.tag == UniversalType::ObjectIdentifier && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
 			std::vector<uint8_t> value(typeHeader.length, 0x00);
 			read(value.data(), typeHeader.length);
-			handlers.writeOID(typeHeader,{value});
+			handlers->writeOID(typeHeader,{value});
 		} else if (typeHeader.tag == UniversalType::UTF8String && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
 			// TODO: check available bytes before allocating buffer
 			std::string s;
 			s.resize(typeHeader.length);
 			read((uint8_t*) s.data(), typeHeader.length);
-			handlers.writeTextString(typeHeader, s);
+			handlers->writeTextString(typeHeader, s);
 		} else if (typeHeader.tag == UniversalType::PrintableString && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
 			// TODO: check encoding
 			// TODO: check available bytes before allocating buffer
 			std::string s;
 			s.resize(typeHeader.length);
 			read((uint8_t*) s.data(), typeHeader.length);
-			handlers.writeTextString(typeHeader, s);
+			handlers->writeTextString(typeHeader, s);
 		} else if (typeHeader.tag == UniversalType::OctetString && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
 			// TODO: check available bytes before allocating buffer
 			std::string s;
 			s.resize(typeHeader.length);
 			read((uint8_t*) s.data(), typeHeader.length);
-			if (processEncapsulatedContent(typeHeader, s) == false) handlers.writeOctetString(typeHeader, s);
+			if (processEncapsulatedContent(typeHeader, s) == false) handlers->writeOctetString(typeHeader, s);
 		} else if (typeHeader.tag == UniversalType::BitString && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
 			// TODO: check available bytes before allocating buffer
 			std::string s;
@@ -185,7 +185,7 @@
 					for (uint8_t j = padding; j < 8; j++) bits.push_back(s.back() & 1 << j);
 					for (size_t i = s.size() - 2; i > 0; i--) for (uint8_t j = 0; j < 8; j++) bits.push_back(s[i] & 1 << j);
 				}
-				handlers.writeBitString(typeHeader, bits);
+				handlers->writeBitString(typeHeader, bits);
 			}
 		} else if (typeHeader.tag == UniversalType::UTCTime && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
 			// TODO: check available bytes before allocating buffer
@@ -212,7 +212,7 @@
 					dateTime.timezoneHour = std::stoi(match[i++]);
 					dateTime.timezoneMinute = std::stoi(match[i++]);
 				}
-				handlers.writeDateTime(typeHeader, dateTime);
+				handlers->writeDateTime(typeHeader, dateTime);
 			} else {
 				throw std::invalid_argument("Unsupported UTCTime format: " + s); // TODO: better exception
 			}
@@ -244,7 +244,7 @@
 					dateTime.timezoneHour = std::stoi(match[i++]);
 					dateTime.timezoneMinute = std::stoi(match[i++]);
 				}
-				handlers.writeDateTime(typeHeader, dateTime);
+				handlers->writeDateTime(typeHeader, dateTime);
 			} else {
 				throw std::invalid_argument("Unsupported GeneralizedTime format: " + s); // TODO: better exception
 			}
@@ -265,7 +265,7 @@
 					<< " definite = " << (typeHeader.definiteLength ? "true" : "false");
 
 			// TODO: special event for unparsed? (instead of a text string)
-			handlers.writeTextString(typeHeader, description.str());
+			handlers->writeTextString(typeHeader, description.str());
 		}
 
 		commit();
@@ -314,18 +314,17 @@
 		// TODO: avoid double parsing + encapsulated content might be also processed at the XML/DOM level where we may even do conditional processing based on XPath (evaluate only certain octet- or bit- strings)
 		// We may also do the same as with SEQUENCE or SET (continue nested reading in this ASN1Rreader instance), but it would require valid encapsulated data and would avoid easy fallback to raw OCTET or BIT STRING. We would also have to check the boundaries of the nested part.
 		if (isValidBER(input)) {
-			handlers.writeCollectionStart(typeHeader);
+			handlers->writeCollectionStart(typeHeader);
 
 			BasicASN1Reader encapsulatedReader;
 			std::shared_ptr<EncapsulatedASN1ContentHandler> encapsulatedHandler = std::make_shared<EncapsulatedASN1ContentHandler>();
-			encapsulatedHandler->addHandler(std::shared_ptr<ASN1ContentHandlerProxy>(&handlers, [](auto doNotDeleteHere) {
-			})); // FIXME: correct memory management
+			encapsulatedHandler->addHandler(handlers);
 			encapsulatedReader.addHandler(encapsulatedHandler);
 
 			encapsulatedReader.write((const uint8_t*) input.c_str(), input.size());
 			encapsulatedReader.close();
 
-			handlers.writeCollectionEnd();
+			handlers->writeCollectionEnd();
 			return true;
 		} else {
 			return false;
@@ -342,12 +341,12 @@
 
 	void close() override {
 		if (hasAvailableForReading()) throw std::logic_error("Unexpected content at the end of the stream"); // TODO: better exception
-		
+
 		// TODO: check also open sequences etc.; maybe in the handler
 
 		checkRemainingItems();
 		// TODO: check the bytes remaining in the buffer
-		if (started) handlers.writeStreamEnd();
+		if (started) handlers->writeStreamEnd();
 	}
 
 };