Fix deallocate for working on old compiers (#1478)

Co-authored-by: Jordan Bayles <bayles.jordan@gmail.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c958250..6104c5c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -93,6 +93,12 @@
     set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" CACHE PATH "Executable/dll output dir.")
 endif()
 
+include(CheckFunctionExists)
+check_function_exists(memset_s HAVE_MEMSET_S)
+if(HAVE_MEMSET_S)
+    add_definitions("-DHAVE_MEMSET_S=1")
+endif()
+
 if(JSONCPP_USE_SECURE_MEMORY)
     add_definitions("-DJSONCPP_USE_SECURE_MEMORY=1")
 endif()
diff --git a/include/json/allocator.h b/include/json/allocator.h
index f4fcc1c..459c34c 100644
--- a/include/json/allocator.h
+++ b/include/json/allocator.h
@@ -6,6 +6,7 @@
 #ifndef JSON_ALLOCATOR_H_INCLUDED
 #define JSON_ALLOCATOR_H_INCLUDED
 
+#include <algorithm>
 #include <cstring>
 #include <memory>
 
@@ -38,8 +39,16 @@
    * The memory block is filled with zeroes before being released.
    */
   void deallocate(pointer p, size_type n) {
-    // memset_s is used because memset may be optimized away by the compiler
+    // These constructs will not be removed by the compiler during optimization,
+    // unlike memset.
+#if defined(HAVE_MEMSET_S)
     memset_s(p, n * sizeof(T), 0, n * sizeof(T));
+#elif defined(_WIN32)
+    RtlSecureZeroMemory(p, n * sizeof(T));
+#else
+    std::fill_n(reinterpret_cast<volatile unsigned char*>(p), n, 0);
+#endif
+
     // free using "global operator delete"
     ::operator delete(p);
   }