diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index 29c2cbb5453a74f1b88cbf1122189769c9438f09..3a28f521d5474a0d1e512befc6f0b501167f119c 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -34,6 +34,9 @@
 #include <iostream>
 #include "apr_base64.h"
 
+#include <boost/iostreams/device/array.hpp>
+#include <boost/iostreams/stream.hpp>
+
 #ifdef LL_USESYSTEMLIBS
 # include <zlib.h>
 #else
@@ -2158,11 +2161,6 @@ std::string zip_llsd(LLSD& data)
 // and deserializes from that copy using LLSDSerialize
 LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, S32 size)
 {
-	U8* result = NULL;
-	U32 cur_size = 0;
-	z_stream strm;
-		
-	constexpr U32 CHUNK = 1024 * 256;
 	std::unique_ptr<U8[]> in;
 	try
 	{
@@ -2174,6 +2172,17 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is,
 	}
 	is.read((char*) in.get(), size); 
 
+	return unzip_llsd(data, in.get(), size);
+}
+
+LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 size)
+{
+	U8* result = NULL;
+	U32 cur_size = 0;
+	z_stream strm;
+		
+	constexpr U32 CHUNK = 1024 * 256;
+
 	static thread_local std::unique_ptr<U8[]> out;
 	if (!out)
 	{
@@ -2191,7 +2200,7 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is,
 	strm.zfree = Z_NULL;
 	strm.opaque = Z_NULL;
 	strm.avail_in = size;
-	strm.next_in = in.get();
+	strm.next_in = const_cast<U8*>(in);
 
 	S32 ret = inflateInit(&strm);
 	switch (ret)
@@ -2253,7 +2262,6 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is,
 	} while (ret == Z_OK && ret != Z_STREAM_END);
 
 	inflateEnd(&strm);
-	in.reset();
 
 	if (ret != Z_STREAM_END)
 	{
@@ -2263,37 +2271,11 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is,
 
 	//result now points to the decompressed LLSD block
 	{
-		std::istringstream istr;
-		// Since we are using this for meshes, data we are dealing with tend to be large.
-		// So string can potentially fail to allocate, make sure this won't cause problems
-		try
-		{
-			std::string res_str((char*)result, cur_size);
-
-			static std::string deprecated_header("<? LLSD/Binary ?>");
-
-			if (res_str.substr(0, deprecated_header.size()) == deprecated_header)
-			{
-				res_str = res_str.substr(deprecated_header.size() + 1, cur_size);
-			}
-			cur_size = res_str.size();
-
-			istr.str(res_str);
-		}
-#ifdef LL_WINDOWS
-		catch (const std::length_error&)
-		{
-			free(result);
-			return ZR_SIZE_ERROR;
-		}
-#endif
-		catch (const std::bad_alloc&)
-		{
-			free(result);
-			return ZR_MEM_ERROR;
-		}
+		char* result_ptr = strip_deprecated_header((char*)result, cur_size);
 
-		if (!LLSDSerialize::fromBinary(data, istr, cur_size, UNZIP_LLSD_MAX_DEPTH))
+		boost::iostreams::stream<boost::iostreams::array_source> istrm(result_ptr, cur_size);
+		
+		if (!LLSDSerialize::fromBinary(data, istrm, cur_size, UNZIP_LLSD_MAX_DEPTH))
 		{
 			free(result);
 			return ZR_PARSE_ERROR;
@@ -2311,15 +2293,8 @@ U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32
 	if (size == 0)
 	{
 		LL_WARNS() << "No data to unzip." << LL_ENDL;
-		return NULL;
+		return nullptr;
 	}
-
-	U8* result = NULL;
-	U32 cur_size = 0;
-	z_stream strm;
-		
-	const U32 CHUNK = 0x4000;
-
 	std::unique_ptr<U8[]> in;
 	try
 	{
@@ -2332,6 +2307,21 @@ U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32
 	}
 
 	is.read((char*) in.get(), size); 
+	return unzip_llsdNavMesh(valid, outsize, in.get(), size);
+}
+
+U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, const U8* in, S32 size )
+{
+	if (size == 0)
+	{
+		LL_WARNS() << "No data to unzip." << LL_ENDL;
+		return nullptr;
+	}
+	U8* result = NULL;
+	U32 cur_size = 0;
+	z_stream strm;
+		
+	const U32 CHUNK = 0x4000;
 
 	U8 out[CHUNK];
 		
@@ -2339,7 +2329,7 @@ U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32
 	strm.zfree = Z_NULL;
 	strm.opaque = Z_NULL;
 	strm.avail_in = size;
-	strm.next_in = in.get();
+	strm.next_in = const_cast<U8*>(in);
 
 	
 	S32 ret = inflateInit2(&strm,  windowBits | ENABLE_ZLIB_GZIP );
@@ -2392,7 +2382,6 @@ U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32
 	} while (ret == Z_OK);
 
 	inflateEnd(&strm);
-	in.reset();
 
 	if (ret != Z_STREAM_END)
 	{
@@ -2410,4 +2399,22 @@ U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32
 	return result;
 }
 
+char* strip_deprecated_header(char* in, U32& cur_size, U32* header_size)
+{
+	const char* deprecated_header = "<? LLSD/Binary ?>";
+	constexpr size_t deprecated_header_size = 17;
+
+	if (cur_size > deprecated_header_size
+		&& memcmp(in, deprecated_header, deprecated_header_size) == 0)
+	{
+		in = in + deprecated_header_size;
+		cur_size = cur_size - deprecated_header_size;
+		if (header_size)
+		{
+			*header_size = deprecated_header_size + 1;
+		}
+	}
+
+	return in;
+}
 
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h
index 8c1dc62e984c4a688224b624b27d4408bfffb38e..5fd210f608e338d36a83d13cff83a9f0242a1151 100644
--- a/indra/llcommon/llsdserialize.h
+++ b/indra/llcommon/llsdserialize.h
@@ -865,6 +865,7 @@ class LL_COMMON_API LLUZipHelper : public LLRefCount
     } EZipRresult;
     // return OK or reason for failure
     static EZipRresult unzip_llsd(LLSD& data, std::istream& is, S32 size);
+	static EZipRresult unzip_llsd(LLSD& data, const U8* in, S32 size);
 };
 
 //dirty little zip functions -- yell at davep
@@ -872,4 +873,8 @@ LL_COMMON_API std::string zip_llsd(LLSD& data);
 
 
 LL_COMMON_API U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize,std::istream& is, S32 size);
+LL_COMMON_API U8* unzip_llsdNavMesh(bool& valid, unsigned int& outsize, const U8* in, S32 size);
+
+// returns a pointer to the array or past the array if the deprecated header exists
+LL_COMMON_API char* strip_deprecated_header(char* in, U32& cur_size, U32* header_size = nullptr);
 #endif // LL_LLSDSERIALIZE_H