Merge pull request #193 from apache/fix_ci

Fix CI buld
diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml
index c1470de..a40c5a7 100644
--- a/.github/workflows/build_cmake.yml
+++ b/.github/workflows/build_cmake.yml
@@ -43,6 +43,7 @@
         uses: actions/checkout@v2
         with:
           submodules: true
+          persist-credentials: false
       - name: Configure
         run: cd build && cmake ..
       - name: Build C++ unit tests
@@ -54,6 +55,6 @@
         with:
           python-version: '3.8' # 3.x grabs latest minor version of python3, but 3.9 not fully supported yet
       - name: Install Python dependencies
-        run: python -m pip install --upgrade pip setuptools wheel numpy tox
+        run: python -m pip install --upgrade pip setuptools wheel numpy tox pytest
       - name: Build and run Python tests
         run: python -m tox
diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml
index e9e3798..63cef24 100644
--- a/.github/workflows/code_coverage.yml
+++ b/.github/workflows/code_coverage.yml
@@ -19,6 +19,7 @@
         uses: actions/checkout@v2
         with:
           submodules: true
+          persist-credentials: false
       - name: Download and install lcov
         run: |
           VERSION="1.14"
diff --git a/hll/include/AuxHashMap-internal.hpp b/hll/include/AuxHashMap-internal.hpp
index 9a8e135..60142ec 100644
--- a/hll/include/AuxHashMap-internal.hpp
+++ b/hll/include/AuxHashMap-internal.hpp
@@ -26,42 +26,28 @@
 namespace datasketches {
 
 template<typename A>
-AuxHashMap<A>::AuxHashMap(int lgAuxArrInts, int lgConfigK)
-  : lgConfigK(lgConfigK),
-    lgAuxArrInts(lgAuxArrInts),
-    auxCount(0) {
-  typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-  const int numItems = 1 << lgAuxArrInts;
-  auxIntArr = intAlloc().allocate(numItems);
-  std::fill(auxIntArr, auxIntArr + numItems, 0);
-}
+AuxHashMap<A>::AuxHashMap(int lgAuxArrInts, int lgConfigK, const A& allocator):
+lgConfigK(lgConfigK),
+lgAuxArrInts(lgAuxArrInts),
+auxCount(0),
+entries(1 << lgAuxArrInts, 0, allocator)
+{}
 
 template<typename A>
-AuxHashMap<A>* AuxHashMap<A>::newAuxHashMap(int lgAuxArrInts, int lgConfigK) {
-  return new (ahmAlloc().allocate(1)) AuxHashMap<A>(lgAuxArrInts, lgConfigK);
-}
-
-template<typename A>
-AuxHashMap<A>::AuxHashMap(const AuxHashMap& that)
-  : lgConfigK(that.lgConfigK),
-    lgAuxArrInts(that.lgAuxArrInts),
-    auxCount(that.auxCount) {
-  typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-  const int numItems = 1 << lgAuxArrInts;
-  auxIntArr = intAlloc().allocate(numItems);
-  std::copy(that.auxIntArr, that.auxIntArr + numItems, auxIntArr);
+AuxHashMap<A>* AuxHashMap<A>::newAuxHashMap(int lgAuxArrInts, int lgConfigK, const A& allocator) {
+  return new (ahmAlloc(allocator).allocate(1)) AuxHashMap<A>(lgAuxArrInts, lgConfigK, allocator);
 }
 
 template<typename A>
 AuxHashMap<A>* AuxHashMap<A>::newAuxHashMap(const AuxHashMap& that) {
-  return new (ahmAlloc().allocate(1)) AuxHashMap<A>(that);
+  return new (ahmAlloc(that.entries.get_allocator()).allocate(1)) AuxHashMap<A>(that);
 }
 
 template<typename A>
 AuxHashMap<A>* AuxHashMap<A>::deserialize(const void* bytes, size_t len,
                                           int lgConfigK,
                                           int auxCount, int lgAuxArrInts,
-                                          bool srcCompact) {
+                                          bool srcCompact, const A& allocator) {
   int lgArrInts = lgAuxArrInts;
   if (srcCompact) { // early compact versions didn't use LgArr byte field so ignore input
     lgArrInts = HllUtil<A>::computeLgArrInts(HLL, auxCount, lgConfigK);
@@ -77,7 +63,7 @@
     if (len < auxCount * sizeof(int)) {
       throw std::out_of_range("Input array too small to hold AuxHashMap image");
     }
-    auxHashMap = new (ahmAlloc().allocate(1)) AuxHashMap<A>(lgArrInts, lgConfigK);
+    auxHashMap = new (ahmAlloc(allocator).allocate(1)) AuxHashMap<A>(lgArrInts, lgConfigK, allocator);
     for (int i = 0; i < auxCount; ++i) {
       int pair = auxPtr[i];
       int slotNo = HllUtil<A>::getLow26(pair) & configKmask;
@@ -89,7 +75,7 @@
     if (len < itemsToRead * sizeof(int)) {
       throw std::out_of_range("Input array too small to hold AuxHashMap image");
     }
-    auxHashMap = new (ahmAlloc().allocate(1)) AuxHashMap<A>(lgArrInts, lgConfigK);
+    auxHashMap = new (ahmAlloc(allocator).allocate(1)) AuxHashMap<A>(lgArrInts, lgConfigK, allocator);
     for (int i = 0; i < itemsToRead; ++i) {
       int pair = auxPtr[i];
       if (pair == HllUtil<A>::EMPTY) { continue; }
@@ -110,7 +96,7 @@
 template<typename A>
 AuxHashMap<A>* AuxHashMap<A>::deserialize(std::istream& is, const int lgConfigK,
                                           const int auxCount, const int lgAuxArrInts,
-                                          const bool srcCompact) {
+                                          const bool srcCompact, const A& allocator) {
   int lgArrInts = lgAuxArrInts;
   if (srcCompact) { // early compact versions didn't use LgArr byte field so ignore input
     lgArrInts = HllUtil<A>::computeLgArrInts(HLL, auxCount, lgConfigK);
@@ -118,7 +104,7 @@
     lgArrInts = lgAuxArrInts;
   }
 
-  AuxHashMap<A>* auxHashMap = new (ahmAlloc().allocate(1)) AuxHashMap<A>(lgArrInts, lgConfigK);
+  AuxHashMap<A>* auxHashMap = new (ahmAlloc(allocator).allocate(1)) AuxHashMap<A>(lgArrInts, lgConfigK, allocator);
   typedef std::unique_ptr<AuxHashMap<A>, std::function<void(AuxHashMap<A>*)>> aux_hash_map_ptr;
   aux_hash_map_ptr aux_ptr(auxHashMap, auxHashMap->make_deleter());
 
@@ -153,23 +139,17 @@
 }
 
 template<typename A>
-AuxHashMap<A>::~AuxHashMap<A>() {
-  // should be no way to have an object without a valid array
-  typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-  intAlloc().deallocate(auxIntArr, 1 << lgAuxArrInts);
-}
-
-template<typename A>
 std::function<void(AuxHashMap<A>*)> AuxHashMap<A>::make_deleter() {
   return [](AuxHashMap<A>* ptr) {
+    ahmAlloc alloc(ptr->entries.get_allocator());
     ptr->~AuxHashMap();
-    ahmAlloc().deallocate(ptr, 1);
+    alloc.deallocate(ptr, 1);
   };
 }
 
 template<typename A>
 AuxHashMap<A>* AuxHashMap<A>::copy() const {
-  return new (ahmAlloc().allocate(1)) AuxHashMap<A>(*this);
+  return new (ahmAlloc(entries.get_allocator()).allocate(1)) AuxHashMap<A>(*this);
 }
 
 template<typename A>
@@ -179,7 +159,7 @@
 
 template<typename A>
 int* AuxHashMap<A>::getAuxIntArr(){
-  return auxIntArr;
+  return entries.data();
 }
 
 template<typename A>
@@ -199,7 +179,7 @@
 
 template<typename A>
 void AuxHashMap<A>::mustAdd(const int slotNo, const int value) {
-  const int index = find(auxIntArr, lgAuxArrInts, lgConfigK, slotNo);
+  const int index = find(entries.data(), lgAuxArrInts, lgConfigK, slotNo);
   const int entry_pair = HllUtil<A>::pair(slotNo, value);
   if (index >= 0) {
     throw std::invalid_argument("Found a slotNo that should not be there: SlotNo: "
@@ -207,16 +187,16 @@
   }
 
   // found empty entry
-  auxIntArr[~index] = entry_pair;
+  entries[~index] = entry_pair;
   ++auxCount;
   checkGrow();
 }
 
 template<typename A>
 int AuxHashMap<A>::mustFindValueFor(const int slotNo) const {
-  const int index = find(auxIntArr, lgAuxArrInts, lgConfigK, slotNo);
+  const int index = find(entries.data(), lgAuxArrInts, lgConfigK, slotNo);
   if (index >= 0) {
-    return HllUtil<A>::getValue(auxIntArr[index]);
+    return HllUtil<A>::getValue(entries[index]);
   }
 
   throw std::invalid_argument("slotNo not found: " + std::to_string(slotNo));
@@ -224,9 +204,9 @@
 
 template<typename A>
 void AuxHashMap<A>::mustReplace(const int slotNo, const int value) {
-  const int idx = find(auxIntArr, lgAuxArrInts, lgConfigK, slotNo);
+  const int idx = find(entries.data(), lgAuxArrInts, lgConfigK, slotNo);
   if (idx >= 0) {
-    auxIntArr[idx] = HllUtil<A>::pair(slotNo, value);
+    entries[idx] = HllUtil<A>::pair(slotNo, value);
     return;
   }
 
@@ -243,23 +223,18 @@
 
 template<typename A>
 void AuxHashMap<A>::growAuxSpace() {
-  int* oldArray = auxIntArr;
-  const int oldArrLen = 1 << lgAuxArrInts;
   const int configKmask = (1 << lgConfigK) - 1;
   const int newArrLen = 1 << ++lgAuxArrInts;
-  typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-  auxIntArr = intAlloc().allocate(newArrLen);
-  std::fill(auxIntArr, auxIntArr + newArrLen, 0);
-  for (int i = 0; i < oldArrLen; ++i) {
-    const int fetched = oldArray[i];
+  vector_int entries_new(newArrLen, 0, entries.get_allocator());
+  for (size_t i = 0; i < entries.size(); ++i) {
+    const int fetched = entries[i];
     if (fetched != HllUtil<A>::EMPTY) {
       // find empty in new array
-      const int idx = find(auxIntArr, lgAuxArrInts, lgConfigK, fetched & configKmask);
-      auxIntArr[~idx] = fetched;
+      const int idx = find(entries_new.data(), lgAuxArrInts, lgConfigK, fetched & configKmask);
+      entries_new[~idx] = fetched;
     }
   }
-
-  intAlloc().deallocate(oldArray, oldArrLen);
+  entries = std::move(entries_new);
 }
 
 //Searches the Aux arr hash table for an empty or a matching slotNo depending on the context.
@@ -290,12 +265,12 @@
 
 template<typename A>
 coupon_iterator<A> AuxHashMap<A>::begin(bool all) const {
-  return coupon_iterator<A>(auxIntArr, 1 << lgAuxArrInts, 0, all);
+  return coupon_iterator<A>(entries.data(), 1 << lgAuxArrInts, 0, all);
 }
 
 template<typename A>
 coupon_iterator<A> AuxHashMap<A>::end() const {
-  return coupon_iterator<A>(auxIntArr, 1 << lgAuxArrInts, 1 << lgAuxArrInts, false);
+  return coupon_iterator<A>(entries.data(), 1 << lgAuxArrInts, 1 << lgAuxArrInts, false);
 }
 
 }
diff --git a/hll/include/AuxHashMap.hpp b/hll/include/AuxHashMap.hpp
index b37e85c..e18f15d 100644
--- a/hll/include/AuxHashMap.hpp
+++ b/hll/include/AuxHashMap.hpp
@@ -28,22 +28,21 @@
 
 namespace datasketches {
 
-template<typename A = std::allocator<char>>
+template<typename A>
 class AuxHashMap final {
   public:
-    explicit AuxHashMap(int lgAuxArrInts, int lgConfigK);
-    explicit AuxHashMap(const AuxHashMap<A>& that);
-    static AuxHashMap* newAuxHashMap(int lgAuxArrInts, int lgConfigK);
+    AuxHashMap(int lgAuxArrInts, int lgConfigK, const A& allocator);
+    static AuxHashMap* newAuxHashMap(int lgAuxArrInts, int lgConfigK, const A& allocator);
     static AuxHashMap* newAuxHashMap(const AuxHashMap<A>& that);
 
     static AuxHashMap* deserialize(const void* bytes, size_t len,
                                    int lgConfigK,
                                    int auxCount, int lgAuxArrInts,
-                                   bool srcCompact);
+                                   bool srcCompact, const A& allocator);
     static AuxHashMap* deserialize(std::istream& is, int lgConfigK,
                                    int auxCount, int lgAuxArrInts,
-                                   bool srcCompact);
-    virtual ~AuxHashMap();
+                                   bool srcCompact, const A& allocator);
+    virtual ~AuxHashMap() = default;
     static std::function<void(AuxHashMap<A>*)> make_deleter();
     
     AuxHashMap* copy() const;
@@ -64,6 +63,8 @@
   private:
     typedef typename std::allocator_traits<A>::template rebind_alloc<AuxHashMap<A>> ahmAlloc;
 
+    using vector_int = std::vector<int, typename std::allocator_traits<A>::template rebind_alloc<int>>;
+
     // static so it can be used when resizing
     static int find(const int* auxArr, int lgAuxArrInts, int lgConfigK, int slotNo);
 
@@ -73,7 +74,7 @@
     const int lgConfigK;
     int lgAuxArrInts;
     int auxCount;
-    int* auxIntArr;
+    vector_int entries;
 };
 
 }
diff --git a/hll/include/CompositeInterpolationXTable.hpp b/hll/include/CompositeInterpolationXTable.hpp
index 8baecbe..0fa0af8 100644
--- a/hll/include/CompositeInterpolationXTable.hpp
+++ b/hll/include/CompositeInterpolationXTable.hpp
@@ -24,7 +24,7 @@
 
 namespace datasketches {
 
-template<typename A = std::allocator<char>>
+template<typename A = std::allocator<uint8_t>>
 class CompositeInterpolationXTable {
   public:
     static int get_y_stride(int logK);
@@ -37,4 +37,4 @@
 
 #include "CompositeInterpolationXTable-internal.hpp"
 
-#endif /* _COMPOSITEINTERPOLATIONXTABLE_HPP_ */
\ No newline at end of file
+#endif /* _COMPOSITEINTERPOLATIONXTABLE_HPP_ */
diff --git a/hll/include/CouponHashSet-internal.hpp b/hll/include/CouponHashSet-internal.hpp
index 35facfe..29a3ea7 100644
--- a/hll/include/CouponHashSet-internal.hpp
+++ b/hll/include/CouponHashSet-internal.hpp
@@ -31,8 +31,8 @@
 static int find(const int* array, const int lgArrInts, const int coupon);
 
 template<typename A>
-CouponHashSet<A>::CouponHashSet(const int lgConfigK, const target_hll_type tgtHllType)
-  : CouponList<A>(lgConfigK, tgtHllType, hll_mode::SET)
+CouponHashSet<A>::CouponHashSet(const int lgConfigK, const target_hll_type tgtHllType, const A& allocator)
+  : CouponList<A>(lgConfigK, tgtHllType, hll_mode::SET, allocator)
 {
   if (lgConfigK <= 7) {
     throw std::invalid_argument("CouponHashSet must be initialized with lgConfigK > 7. Found: "
@@ -41,27 +41,21 @@
 }
 
 template<typename A>
-CouponHashSet<A>::CouponHashSet(const CouponHashSet<A>& that)
-  : CouponList<A>(that) {}
-
-template<typename A>
 CouponHashSet<A>::CouponHashSet(const CouponHashSet<A>& that, const target_hll_type tgtHllType)
   : CouponList<A>(that, tgtHllType) {}
 
 template<typename A>
-CouponHashSet<A>::~CouponHashSet() {}
-
-template<typename A>
 std::function<void(HllSketchImpl<A>*)> CouponHashSet<A>::get_deleter() const {
   return [](HllSketchImpl<A>* ptr) {
     CouponHashSet<A>* chs = static_cast<CouponHashSet<A>*>(ptr);
+    ChsAlloc chsa(chs->getAllocator());
     chs->~CouponHashSet();
-    chsAlloc().deallocate(chs, 1);
+    chsa.deallocate(chs, 1);
   };
 }
 
 template<typename A>
-CouponHashSet<A>* CouponHashSet<A>::newSet(const void* bytes, size_t len) {
+CouponHashSet<A>* CouponHashSet<A>::newSet(const void* bytes, size_t len, const A& allocator) {
   if (len < HllUtil<A>::HASH_SET_INT_ARR_START) { // hard-coded 
     throw std::out_of_range("Input data length insufficient to hold CouponHashSet");
   }
@@ -79,7 +73,7 @@
 
   const hll_mode mode = HllSketchImpl<A>::extractCurMode(data[HllUtil<A>::MODE_BYTE]);
   if (mode != SET) {
-    throw std::invalid_argument("Calling set construtor with non-set mode data");
+    throw std::invalid_argument("Calling set constructor with non-set mode data");
   }
 
   const target_hll_type tgtHllType = HllSketchImpl<A>::extractTgtHllType(data[HllUtil<A>::MODE_BYTE]);
@@ -106,7 +100,8 @@
                                 + ", found: " + std::to_string(len));
   }
 
-  CouponHashSet<A>* sketch = new (chsAlloc().allocate(1)) CouponHashSet<A>(lgK, tgtHllType);
+  ChsAlloc chsa(allocator);
+  CouponHashSet<A>* sketch = new (chsa.allocate(1)) CouponHashSet<A>(lgK, tgtHllType, allocator);
 
   if (compactFlag) {
     const uint8_t* curPos = data + HllUtil<A>::HASH_SET_INT_ARR_START;
@@ -116,24 +111,19 @@
       sketch->couponUpdate(coupon);
     }
   } else {
-    int* oldArr = sketch->couponIntArr;
-    const size_t oldArrLen = 1 << sketch->lgCouponArrInts;
-    sketch->lgCouponArrInts = lgArrInts;
-    typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-    sketch->couponIntArr = intAlloc().allocate(1 << lgArrInts);
+    sketch->coupons.resize(1 << lgArrInts);
     sketch->couponCount = couponCount;
     // only need to read valid coupons, unlike in stream case
-    std::memcpy(sketch->couponIntArr,
+    std::memcpy(sketch->coupons.data(),
                 data + HllUtil<A>::HASH_SET_INT_ARR_START,
                 couponCount * sizeof(int));
-    intAlloc().deallocate(oldArr, oldArrLen);
   }
 
   return sketch;
 }
 
 template<typename A>
-CouponHashSet<A>* CouponHashSet<A>::newSet(std::istream& is) {
+CouponHashSet<A>* CouponHashSet<A>::newSet(std::istream& is, const A& allocator) {
   uint8_t listHeader[8];
   is.read((char*)listHeader, 8 * sizeof(uint8_t));
 
@@ -149,7 +139,7 @@
 
   hll_mode mode = HllSketchImpl<A>::extractCurMode(listHeader[HllUtil<A>::MODE_BYTE]);
   if (mode != SET) {
-    throw std::invalid_argument("Calling set construtor with non-set mode data");
+    throw std::invalid_argument("Calling set constructor with non-set mode data");
   }
 
   target_hll_type tgtHllType = HllSketchImpl<A>::extractTgtHllType(listHeader[HllUtil<A>::MODE_BYTE]);
@@ -168,7 +158,8 @@
     lgArrInts = HllUtil<A>::computeLgArrInts(SET, couponCount, lgK);
   }
 
-  CouponHashSet<A>* sketch = new (chsAlloc().allocate(1)) CouponHashSet<A>(lgK, tgtHllType);
+  ChsAlloc chsa(allocator);
+  CouponHashSet<A>* sketch = new (chsa.allocate(1)) CouponHashSet<A>(lgK, tgtHllType, allocator);
   typedef std::unique_ptr<CouponHashSet<A>, std::function<void(HllSketchImpl<A>*)>> coupon_hash_set_ptr;
   coupon_hash_set_ptr ptr(sketch, sketch->get_deleter());
 
@@ -181,13 +172,10 @@
       sketch->couponUpdate(coupon);
     }
   } else {
-    typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-    intAlloc().deallocate(sketch->couponIntArr, 1 << sketch->lgCouponArrInts);
-    sketch->lgCouponArrInts = lgArrInts;
-    sketch->couponIntArr = intAlloc().allocate(1 << lgArrInts);
+    sketch->coupons.resize(1 << lgArrInts);
     sketch->couponCount = couponCount;
     // for stream processing, read entire list so read pointer ends up set correctly
-    is.read((char*)sketch->couponIntArr, (1 << sketch->lgCouponArrInts) * sizeof(int));
+    is.read((char*)sketch->coupons.data(), sketch->coupons.size() * sizeof(int));
   } 
 
   if (!is.good())
@@ -198,21 +186,24 @@
 
 template<typename A>
 CouponHashSet<A>* CouponHashSet<A>::copy() const {
-  return new (chsAlloc().allocate(1)) CouponHashSet<A>(*this);
+  ChsAlloc chsa(this->coupons.get_allocator());
+  return new (chsa.allocate(1)) CouponHashSet<A>(*this);
 }
 
 template<typename A>
 CouponHashSet<A>* CouponHashSet<A>::copyAs(const target_hll_type tgtHllType) const {
-  return new (chsAlloc().allocate(1)) CouponHashSet<A>(*this, tgtHllType);
+  ChsAlloc chsa(this->coupons.get_allocator());
+  return new (chsa.allocate(1)) CouponHashSet<A>(*this, tgtHllType);
 }
 
 template<typename A>
 HllSketchImpl<A>* CouponHashSet<A>::couponUpdate(int coupon) {
-  const int index = find<A>(this->couponIntArr, this->lgCouponArrInts, coupon);
+  const uint8_t lgCouponArrInts = count_trailing_zeros_in_u32(this->coupons.size());
+  const int index = find<A>(this->coupons.data(), lgCouponArrInts, coupon);
   if (index >= 0) {
     return this; // found duplicate, ignore
   }
-  this->couponIntArr[~index] = coupon; // found empty
+  this->coupons[~index] = coupon; // found empty
   ++this->couponCount;
   if (checkGrowOrPromote()) {
     return this->promoteHeapListOrSetToHll(*this);
@@ -232,39 +223,34 @@
 
 template<typename A>
 bool CouponHashSet<A>::checkGrowOrPromote() {
-  if ((HllUtil<A>::RESIZE_DENOM * this->couponCount) > (HllUtil<A>::RESIZE_NUMER * (1 << this->lgCouponArrInts))) {
-    if (this->lgCouponArrInts == (this->lgConfigK - 3)) { // at max size
+  if (static_cast<size_t>(HllUtil<A>::RESIZE_DENOM * this->couponCount) > (HllUtil<A>::RESIZE_NUMER * this->coupons.size())) {
+    const uint8_t lgCouponArrInts = count_trailing_zeros_in_u32(this->coupons.size());
+    if (lgCouponArrInts == (this->lgConfigK - 3)) { // at max size
       return true; // promote to HLL
     }
-    int tgtLgCoupArrSize = this->lgCouponArrInts + 1;
-    growHashSet(this->lgCouponArrInts, tgtLgCoupArrSize);
+    growHashSet(lgCouponArrInts + 1);
   }
   return false;
 }
 
 template<typename A>
-void CouponHashSet<A>::growHashSet(const int srcLgCoupArrSize, const int tgtLgCoupArrSize) {
+void CouponHashSet<A>::growHashSet(int tgtLgCoupArrSize) {
   const int tgtLen = 1 << tgtLgCoupArrSize;
-  typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-  int* tgtCouponIntArr = intAlloc().allocate(tgtLen);
-  std::fill(tgtCouponIntArr, tgtCouponIntArr + tgtLen, 0);
+  vector_int coupons_new(tgtLen, 0, this->coupons.get_allocator());
 
-  const int srcLen = 1 << srcLgCoupArrSize;
+  const int srcLen = this->coupons.size();
   for (int i = 0; i < srcLen; ++i) { // scan existing array for non-zero values
-    const int fetched = this->couponIntArr[i];
+    const int fetched = this->coupons[i];
     if (fetched != HllUtil<A>::EMPTY) {
-      const int idx = find<A>(tgtCouponIntArr, tgtLgCoupArrSize, fetched); // search TGT array
+      const int idx = find<A>(coupons_new.data(), tgtLgCoupArrSize, fetched); // search TGT array
       if (idx < 0) { // found EMPTY
-        tgtCouponIntArr[~idx] = fetched; // insert
+        coupons_new[~idx] = fetched; // insert
         continue;
       }
       throw std::runtime_error("Error: Found duplicate coupon");
     }
   }
-
-  intAlloc().deallocate(this->couponIntArr, 1 << this->lgCouponArrInts);
-  this->couponIntArr = tgtCouponIntArr;
-  this->lgCouponArrInts = tgtLgCoupArrSize;
+  this->coupons = std::move(coupons_new);
 }
 
 template<typename A>
diff --git a/hll/include/CouponHashSet.hpp b/hll/include/CouponHashSet.hpp
index 7aaffc3..b9b99b7 100644
--- a/hll/include/CouponHashSet.hpp
+++ b/hll/include/CouponHashSet.hpp
@@ -24,20 +24,20 @@
 
 namespace datasketches {
 
-template<typename A = std::allocator<char>>
+template<typename A>
 class CouponHashSet : public CouponList<A> {
   public:
-    static CouponHashSet* newSet(const void* bytes, size_t len);
-    static CouponHashSet* newSet(std::istream& is);
-    explicit CouponHashSet(int lgConfigK, target_hll_type tgtHllType);
-    explicit CouponHashSet(const CouponHashSet& that, target_hll_type tgtHllType);
-    explicit CouponHashSet(const CouponHashSet& that);
+    static CouponHashSet* newSet(const void* bytes, size_t len, const A& allocator);
+    static CouponHashSet* newSet(std::istream& is, const A& allocator);
+    CouponHashSet(int lgConfigK, target_hll_type tgtHllType, const A& allocator);
+    CouponHashSet(const CouponHashSet& that, target_hll_type tgtHllType);
 
-    virtual ~CouponHashSet();
+    virtual ~CouponHashSet() = default;
     virtual std::function<void(HllSketchImpl<A>*)> get_deleter() const;
 
   protected:
-    
+    using vector_int = std::vector<int, typename std::allocator_traits<A>::template rebind_alloc<int>>;
+
     virtual CouponHashSet* copy() const;
     virtual CouponHashSet* copyAs(target_hll_type tgtHllType) const;
 
@@ -49,9 +49,9 @@
     friend class HllSketchImplFactory<A>;
 
   private:
-    typedef typename std::allocator_traits<A>::template rebind_alloc<CouponHashSet<A>> chsAlloc;
+    using ChsAlloc = typename std::allocator_traits<A>::template rebind_alloc<CouponHashSet<A>>;
     bool checkGrowOrPromote();
-    void growHashSet(int srcLgCoupArrSize, int tgtLgCoupArrSize);
+    void growHashSet(int tgtLgCoupArrSize);
 };
 
 }
diff --git a/hll/include/CouponList-internal.hpp b/hll/include/CouponList-internal.hpp
index 1800a37..fd304c8 100644
--- a/hll/include/CouponList-internal.hpp
+++ b/hll/include/CouponList-internal.hpp
@@ -23,6 +23,7 @@
 #include "CouponList.hpp"
 #include "CubicInterpolation.hpp"
 #include "HllUtil.hpp"
+#include "count_zeros.hpp"
 
 #include <algorithm>
 #include <cmath>
@@ -30,74 +31,45 @@
 namespace datasketches {
 
 template<typename A>
-CouponList<A>::CouponList(const int lgConfigK, const target_hll_type tgtHllType, const hll_mode mode)
-  : HllSketchImpl<A>(lgConfigK, tgtHllType, mode, false) {
-    if (mode == hll_mode::LIST) {
-      lgCouponArrInts = HllUtil<A>::LG_INIT_LIST_SIZE;
-    } else { // mode == SET
-      lgCouponArrInts = HllUtil<A>::LG_INIT_SET_SIZE;
-    }
-    oooFlag = false;
-    const int arrayLen = 1 << lgCouponArrInts;
-    typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-    couponIntArr = intAlloc().allocate(arrayLen);
-    std::fill(couponIntArr, couponIntArr + arrayLen, 0);
-    couponCount = 0;
-}
+CouponList<A>::CouponList(const int lgConfigK, const target_hll_type tgtHllType, const hll_mode mode, const A& allocator):
+HllSketchImpl<A>(lgConfigK, tgtHllType, mode, false),
+couponCount(0),
+oooFlag(false),
+coupons(1 << (mode == hll_mode::LIST ? HllUtil<A>::LG_INIT_LIST_SIZE : HllUtil<A>::LG_INIT_SET_SIZE), 0, allocator)
+{}
 
 template<typename A>
-CouponList<A>::CouponList(const CouponList& that)
-  : HllSketchImpl<A>(that.lgConfigK, that.tgtHllType, that.mode, false),
-    lgCouponArrInts(that.lgCouponArrInts),
-    couponCount(that.couponCount),
-    oooFlag(that.oooFlag) {
-
-  const int numItems = 1 << lgCouponArrInts;
-  typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-  couponIntArr = intAlloc().allocate(numItems);
-  std::copy(that.couponIntArr, that.couponIntArr + numItems, couponIntArr);
-}
-
-template<typename A>
-CouponList<A>::CouponList(const CouponList& that, const target_hll_type tgtHllType)
-  : HllSketchImpl<A>(that.lgConfigK, tgtHllType, that.mode, false),
-    lgCouponArrInts(that.lgCouponArrInts),
-    couponCount(that.couponCount),
-    oooFlag(that.oooFlag) {
-
-  const int numItems = 1 << lgCouponArrInts;
-  typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-  couponIntArr = intAlloc().allocate(numItems);
-  std::copy(that.couponIntArr, that.couponIntArr + numItems, couponIntArr);
-}
-
-template<typename A>
-CouponList<A>::~CouponList() {
-  typedef typename std::allocator_traits<A>::template rebind_alloc<int> intAlloc;
-  intAlloc().deallocate(couponIntArr, 1 << lgCouponArrInts);
-}
+CouponList<A>::CouponList(const CouponList& that, const target_hll_type tgtHllType):
+HllSketchImpl<A>(that.lgConfigK, tgtHllType, that.mode, false),
+couponCount(that.couponCount),
+oooFlag(that.oooFlag),
+coupons(that.coupons)
+{}
 
 template<typename A>
 std::function<void(HllSketchImpl<A>*)> CouponList<A>::get_deleter() const {
   return [](HllSketchImpl<A>* ptr) {
     CouponList<A>* cl = static_cast<CouponList<A>*>(ptr);
+    ClAlloc cla(cl->getAllocator());
     cl->~CouponList();
-    clAlloc().deallocate(cl, 1);
+    cla.deallocate(cl, 1);
   };
 }
 
 template<typename A>
 CouponList<A>* CouponList<A>::copy() const {
-  return new (clAlloc().allocate(1)) CouponList<A>(*this);
+  ClAlloc cla(coupons.get_allocator());
+  return new (cla.allocate(1)) CouponList<A>(*this);
 }
 
 template<typename A>
 CouponList<A>* CouponList<A>::copyAs(target_hll_type tgtHllType) const {
-  return new (clAlloc().allocate(1)) CouponList<A>(*this, tgtHllType);
+  ClAlloc cla(coupons.get_allocator());
+  return new (cla.allocate(1)) CouponList<A>(*this, tgtHllType);
 }
 
 template<typename A>
-CouponList<A>* CouponList<A>::newList(const void* bytes, size_t len) {
+CouponList<A>* CouponList<A>::newList(const void* bytes, size_t len, const A& allocator) {
   if (len < HllUtil<A>::LIST_INT_ARR_START) {
     throw std::out_of_range("Input data length insufficient to hold CouponHashSet");
   }
@@ -115,7 +87,7 @@
 
   hll_mode mode = HllSketchImpl<A>::extractCurMode(data[HllUtil<A>::MODE_BYTE]);
   if (mode != LIST) {
-    throw std::invalid_argument("Calling set construtor with non-list mode data");
+    throw std::invalid_argument("Calling list constructor with non-list mode data");
   }
 
   target_hll_type tgtHllType = HllSketchImpl<A>::extractTgtHllType(data[HllUtil<A>::MODE_BYTE]);
@@ -133,20 +105,21 @@
                                 + ", found: " + std::to_string(len));
   }
 
-  CouponList<A>* sketch = new (clAlloc().allocate(1)) CouponList<A>(lgK, tgtHllType, mode);
+  ClAlloc cla(allocator);
+  CouponList<A>* sketch = new (cla.allocate(1)) CouponList<A>(lgK, tgtHllType, mode, allocator);
   sketch->couponCount = couponCount;
   sketch->putOutOfOrderFlag(oooFlag); // should always be false for LIST
 
   if (!emptyFlag) {
     // only need to read valid coupons, unlike in stream case
-    std::memcpy(sketch->couponIntArr, data + HllUtil<A>::LIST_INT_ARR_START, couponCount * sizeof(int));
+    std::memcpy(sketch->coupons.data(), data + HllUtil<A>::LIST_INT_ARR_START, couponCount * sizeof(int));
   }
   
   return sketch;
 }
 
 template<typename A>
-CouponList<A>* CouponList<A>::newList(std::istream& is) {
+CouponList<A>* CouponList<A>::newList(std::istream& is, const A& allocator) {
   uint8_t listHeader[8];
   is.read((char*)listHeader, 8 * sizeof(uint8_t));
 
@@ -162,7 +135,7 @@
 
   hll_mode mode = HllSketchImpl<A>::extractCurMode(listHeader[HllUtil<A>::MODE_BYTE]);
   if (mode != LIST) {
-    throw std::invalid_argument("Calling list construtor with non-list mode data");
+    throw std::invalid_argument("Calling list constructor with non-list mode data");
   }
 
   const target_hll_type tgtHllType = HllSketchImpl<A>::extractTgtHllType(listHeader[HllUtil<A>::MODE_BYTE]);
@@ -172,8 +145,9 @@
   const bool oooFlag = ((listHeader[HllUtil<A>::FLAGS_BYTE] & HllUtil<A>::OUT_OF_ORDER_FLAG_MASK) ? true : false);
   const bool emptyFlag = ((listHeader[HllUtil<A>::FLAGS_BYTE] & HllUtil<A>::EMPTY_FLAG_MASK) ? true : false);
 
-  CouponList<A>* sketch = new (clAlloc().allocate(1)) CouponList<A>(lgK, tgtHllType, mode);
-  typedef std::unique_ptr<CouponList<A>, std::function<void(HllSketchImpl<A>*)>> coupon_list_ptr;
+  ClAlloc cla(allocator);
+  CouponList<A>* sketch = new (cla.allocate(1)) CouponList<A>(lgK, tgtHllType, mode, allocator);
+  using coupon_list_ptr = std::unique_ptr<CouponList<A>, std::function<void(HllSketchImpl<A>*)>>;
   coupon_list_ptr ptr(sketch, sketch->get_deleter());
   const int couponCount = listHeader[HllUtil<A>::LIST_COUNT_BYTE];
   sketch->couponCount = couponCount;
@@ -183,8 +157,8 @@
     // For stream processing, need to read entire number written to stream so read
     // pointer ends up set correctly.
     // If not compact, still need to read empty items even though in order.
-    const int numToRead = (compact ? couponCount : (1 << sketch->lgCouponArrInts));
-    is.read((char*)sketch->couponIntArr, numToRead * sizeof(int));
+    const int numToRead = (compact ? couponCount : sketch->coupons.size());
+    is.read((char*)sketch->coupons.data(), numToRead * sizeof(int));
   }
 
   if (!is.good())
@@ -196,14 +170,14 @@
 template<typename A>
 vector_u8<A> CouponList<A>::serialize(bool compact, unsigned header_size_bytes) const {
   const size_t sketchSizeBytes = (compact ? getCompactSerializationBytes() : getUpdatableSerializationBytes()) + header_size_bytes;
-  vector_u8<A> byteArr(sketchSizeBytes);
+  vector_u8<A> byteArr(sketchSizeBytes, 0, getAllocator());
   uint8_t* bytes = byteArr.data() + header_size_bytes;
 
   bytes[HllUtil<A>::PREAMBLE_INTS_BYTE] = static_cast<uint8_t>(getPreInts());
   bytes[HllUtil<A>::SER_VER_BYTE] = static_cast<uint8_t>(HllUtil<A>::SER_VER);
   bytes[HllUtil<A>::FAMILY_BYTE] = static_cast<uint8_t>(HllUtil<A>::FAMILY_ID);
   bytes[HllUtil<A>::LG_K_BYTE] = static_cast<uint8_t>(this->lgConfigK);
-  bytes[HllUtil<A>::LG_ARR_BYTE] = static_cast<uint8_t>(lgCouponArrInts);
+  bytes[HllUtil<A>::LG_ARR_BYTE] = count_trailing_zeros_in_u32(coupons.size());
   bytes[HllUtil<A>::FLAGS_BYTE] = this->makeFlagsByte(compact);
   bytes[HllUtil<A>::LIST_COUNT_BYTE] = static_cast<uint8_t>(this->mode == LIST ? couponCount : 0);
   bytes[HllUtil<A>::MODE_BYTE] = this->makeModeByte();
@@ -217,7 +191,7 @@
   const int sw = (isCompact() ? 2 : 0) | (compact ? 1 : 0);
   switch (sw) {
     case 0: { // src updatable, dst updatable
-      std::memcpy(bytes + getMemDataStart(), getCouponIntArr(), (1 << lgCouponArrInts) * sizeof(int));
+      std::memcpy(bytes + getMemDataStart(), coupons.data(), coupons.size() * sizeof(int));
       break;
     }
     case 1: { // src updatable, dst compact
@@ -247,7 +221,7 @@
   os.write((char*)&familyId, sizeof(familyId));
   const uint8_t lgKByte((uint8_t) this->lgConfigK);
   os.write((char*)&lgKByte, sizeof(lgKByte));
-  const uint8_t lgArrIntsByte((uint8_t) lgCouponArrInts);
+  const uint8_t lgArrIntsByte(count_trailing_zeros_in_u32(coupons.size()));
   os.write((char*)&lgArrIntsByte, sizeof(lgArrIntsByte));
   const uint8_t flagsByte(this->makeFlagsByte(compact));
   os.write((char*)&flagsByte, sizeof(flagsByte));
@@ -273,7 +247,7 @@
   const int sw = (isCompact() ? 2 : 0) | (compact ? 1 : 0);
   switch (sw) {
     case 0: { // src updatable, dst updatable
-      os.write((char*)getCouponIntArr(), (1 << lgCouponArrInts) * sizeof(int));
+      os.write((char*)coupons.data(), coupons.size() * sizeof(int));
       break;
     }
     case 1: { // src updatable, dst compact
@@ -292,13 +266,12 @@
 
 template<typename A>
 HllSketchImpl<A>* CouponList<A>::couponUpdate(int coupon) {
-  const int len = 1 << lgCouponArrInts;
-  for (int i = 0; i < len; ++i) { // search for empty slot
-    const int couponAtIdx = couponIntArr[i];
+  for (size_t i = 0; i < coupons.size(); ++i) { // search for empty slot
+    const int couponAtIdx = coupons[i];
     if (couponAtIdx == HllUtil<A>::EMPTY) {
-      couponIntArr[i] = coupon; // the actual update
+      coupons[i] = coupon; // the actual update
       ++couponCount;
-      if (couponCount >= len) { // array full
+      if (couponCount == static_cast<int>(coupons.size())) { // array full
         if (this->lgConfigK < 8) {
           return promoteHeapListOrSetToHll(*this);
         }
@@ -348,7 +321,7 @@
 
 template<typename A>
 int CouponList<A>::getUpdatableSerializationBytes() const {
-  return getMemDataStart() + (4 << getLgCouponArrInts());
+  return getMemDataStart() + coupons.size() * sizeof(int);
 }
 
 template<typename A>
@@ -383,13 +356,8 @@
 }
 
 template<typename A>
-int CouponList<A>::getLgCouponArrInts() const {
-  return lgCouponArrInts;
-}
-
-template<typename A>
-int* CouponList<A>::getCouponIntArr() const {
-  return couponIntArr;
+A CouponList<A>::getAllocator() const {
+  return coupons.get_allocator();
 }
 
 template<typename A>
@@ -404,12 +372,12 @@
 
 template<typename A>
 coupon_iterator<A> CouponList<A>::begin(bool all) const {
-  return coupon_iterator<A>(couponIntArr, 1 << lgCouponArrInts, 0, all);
+  return coupon_iterator<A>(coupons.data(), coupons.size(), 0, all);
 }
 
 template<typename A>
 coupon_iterator<A> CouponList<A>::end() const {
-  return coupon_iterator<A>(couponIntArr, 1 << lgCouponArrInts, 1 << lgCouponArrInts, false);
+  return coupon_iterator<A>(coupons.data(), coupons.size(), coupons.size(), false);
 }
 
 }
diff --git a/hll/include/CouponList.hpp b/hll/include/CouponList.hpp
index 063805b..c19569e 100644
--- a/hll/include/CouponList.hpp
+++ b/hll/include/CouponList.hpp
@@ -30,19 +30,18 @@
 template<typename A>
 class HllSketchImplFactory;
 
-template<typename A = std::allocator<char>>
+template<typename A>
 class CouponList : public HllSketchImpl<A> {
   public:
-    explicit CouponList(int lgConfigK, target_hll_type tgtHllType, hll_mode mode);
-    explicit CouponList(const CouponList& that);
-    explicit CouponList(const CouponList& that, target_hll_type tgtHllType);
+    CouponList(int lgConfigK, target_hll_type tgtHllType, hll_mode mode, const A& allocator);
+    CouponList(const CouponList& that, target_hll_type tgtHllType);
 
-    static CouponList* newList(const void* bytes, size_t len);
-    static CouponList* newList(std::istream& is);
+    static CouponList* newList(const void* bytes, size_t len, const A& allocator);
+    static CouponList* newList(std::istream& is, const A& allocator);
     virtual vector_u8<A> serialize(bool compact, unsigned header_size_bytes) const;
     virtual void serialize(std::ostream& os, bool compact) const;
 
-    virtual ~CouponList();
+    virtual ~CouponList() = default;
     virtual std::function<void(HllSketchImpl<A>*)> get_deleter() const;
 
     virtual CouponList* copy() const;
@@ -62,7 +61,9 @@
     coupon_iterator<A> end() const;
 
   protected:
-    typedef typename std::allocator_traits<A>::template rebind_alloc<CouponList<A>> clAlloc;
+    using ClAlloc = typename std::allocator_traits<A>::template rebind_alloc<CouponList<A>>;
+
+    using vector_int = std::vector<int, typename std::allocator_traits<A>::template rebind_alloc<int>>;
 
     HllSketchImpl<A>* promoteHeapListToSet(CouponList& list);
     HllSketchImpl<A>* promoteHeapListOrSetToHll(CouponList& src);
@@ -75,13 +76,11 @@
     virtual bool isOutOfOrderFlag() const;
     virtual void putOutOfOrderFlag(bool oooFlag);
 
-    virtual int getLgCouponArrInts() const;
-    virtual int* getCouponIntArr() const;
+    virtual A getAllocator() const;
 
-    int lgCouponArrInts;
     int couponCount;
     bool oooFlag;
-    int* couponIntArr;
+    vector_int coupons;
 
     friend class HllSketchImplFactory<A>;
 };
diff --git a/hll/include/CubicInterpolation.hpp b/hll/include/CubicInterpolation.hpp
index b9cdfe7..58fb7d7 100644
--- a/hll/include/CubicInterpolation.hpp
+++ b/hll/include/CubicInterpolation.hpp
@@ -24,7 +24,7 @@
 
 namespace datasketches {
 
-template<typename A = std::allocator<char>>
+template<typename A = std::allocator<uint8_t>>
 class CubicInterpolation {
   public:
     static double usingXAndYTables(const double xArr[], const double yArr[],
@@ -40,4 +40,4 @@
 
 #include "CubicInterpolation-internal.hpp"
 
-#endif /* _CUBICINTERPOLATION_HPP_ */
\ No newline at end of file
+#endif /* _CUBICINTERPOLATION_HPP_ */
diff --git a/hll/include/HarmonicNumbers.hpp b/hll/include/HarmonicNumbers.hpp
index 501ce0c..34b830a 100644
--- a/hll/include/HarmonicNumbers.hpp
+++ b/hll/include/HarmonicNumbers.hpp
@@ -25,7 +25,7 @@
 
 namespace datasketches {
 
-template<typename A = std::allocator<char>>
+template<typename A = std::allocator<uint8_t>>
 class HarmonicNumbers {
   public:
     /**
@@ -45,4 +45,4 @@
 
 #include "HarmonicNumbers-internal.hpp"
 
-#endif /* _HARMONICNUMBERS_HPP_ */
\ No newline at end of file
+#endif /* _HARMONICNUMBERS_HPP_ */
diff --git a/hll/include/Hll4Array-internal.hpp b/hll/include/Hll4Array-internal.hpp
index 8498bb8..f93014a 100644
--- a/hll/include/Hll4Array-internal.hpp
+++ b/hll/include/Hll4Array-internal.hpp
@@ -30,13 +30,12 @@
 namespace datasketches {
 
 template<typename A>
-Hll4Array<A>::Hll4Array(const int lgConfigK, const bool startFullSize) :
-    HllArray<A>(lgConfigK, target_hll_type::HLL_4, startFullSize) {
+Hll4Array<A>::Hll4Array(const int lgConfigK, const bool startFullSize, const A& allocator):
+HllArray<A>(lgConfigK, target_hll_type::HLL_4, startFullSize, allocator),
+auxHashMap(nullptr)
+{
   const int numBytes = this->hll4ArrBytes(lgConfigK);
-  typedef typename std::allocator_traits<A>::template rebind_alloc<uint8_t> uint8Alloc;
-  this->hllByteArr = uint8Alloc().allocate(numBytes);
-  std::fill(this->hllByteArr, this->hllByteArr + numBytes, 0);
-  auxHashMap = nullptr;
+  this->hllByteArr.resize(numBytes, 0);
 }
 
 template<typename A>
@@ -63,17 +62,19 @@
 template<typename A>
 std::function<void(HllSketchImpl<A>*)> Hll4Array<A>::get_deleter() const {
   return [](HllSketchImpl<A>* ptr) {
-    typedef typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>> hll4Alloc;
     Hll4Array<A>* hll = static_cast<Hll4Array<A>*>(ptr);
+    using Hll4Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>>;
+    Hll4Alloc hll4Alloc(hll->getAllocator());
     hll->~Hll4Array();
-    hll4Alloc().deallocate(hll, 1);
+    hll4Alloc.deallocate(hll, 1);
   };
 }
 
 template<typename A>
 Hll4Array<A>* Hll4Array<A>::copy() const {
-  typedef typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>> hll4Alloc;
-  return new (hll4Alloc().allocate(1)) Hll4Array<A>(*this);
+  using Hll4Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>>;
+  Hll4Alloc hll4Alloc(this->getAllocator());
+  return new (hll4Alloc.allocate(1)) Hll4Array<A>(*this);
 }
 
 template<typename A>
@@ -195,7 +196,7 @@
           // added to the exception table
           putSlot(slotNo, HllUtil<A>::AUX_TOKEN);
           if (auxHashMap == nullptr) {
-            auxHashMap = AuxHashMap<A>::newAuxHashMap(HllUtil<A>::LG_AUX_ARR_INTS[this->lgConfigK], this->lgConfigK);
+            auxHashMap = AuxHashMap<A>::newAuxHashMap(HllUtil<A>::LG_AUX_ARR_INTS[this->lgConfigK], this->lgConfigK, this->getAllocator());
           }
           auxHashMap->mustAdd(slotNo, newVal);
         }
@@ -285,7 +286,7 @@
       } else { //newShiftedVal >= AUX_TOKEN
         // the former exception remains an exception, so must be added to the newAuxMap
         if (newAuxMap == nullptr) {
-          newAuxMap = AuxHashMap<A>::newAuxHashMap(HllUtil<A>::LG_AUX_ARR_INTS[this->lgConfigK], this->lgConfigK);
+          newAuxMap = AuxHashMap<A>::newAuxHashMap(HllUtil<A>::LG_AUX_ARR_INTS[this->lgConfigK], this->lgConfigK, this->getAllocator());
         }
         newAuxMap->mustAdd(slotNum, oldActualVal);
       }
@@ -315,12 +316,12 @@
 
 template<typename A>
 typename HllArray<A>::const_iterator Hll4Array<A>::begin(bool all) const {
-  return typename HllArray<A>::const_iterator(this->hllByteArr, 1 << this->lgConfigK, 0, this->tgtHllType, auxHashMap, this->curMin, all);
+  return typename HllArray<A>::const_iterator(this->hllByteArr.data(), 1 << this->lgConfigK, 0, this->tgtHllType, auxHashMap, this->curMin, all);
 }
 
 template<typename A>
 typename HllArray<A>::const_iterator Hll4Array<A>::end() const {
-  return typename HllArray<A>::const_iterator(this->hllByteArr, 1 << this->lgConfigK, 1 << this->lgConfigK, this->tgtHllType, auxHashMap, this->curMin, false);
+  return typename HllArray<A>::const_iterator(this->hllByteArr.data(), 1 << this->lgConfigK, 1 << this->lgConfigK, this->tgtHllType, auxHashMap, this->curMin, false);
 }
 
 template<typename A>
diff --git a/hll/include/Hll4Array.hpp b/hll/include/Hll4Array.hpp
index ff56c86..38b2c94 100644
--- a/hll/include/Hll4Array.hpp
+++ b/hll/include/Hll4Array.hpp
@@ -31,7 +31,7 @@
 template<typename A>
 class Hll4Array final : public HllArray<A> {
   public:
-    explicit Hll4Array(int lgConfigK, bool startFullSize);
+    explicit Hll4Array(int lgConfigK, bool startFullSize, const A& allocator);
     explicit Hll4Array(const Hll4Array<A>& that);
 
     virtual ~Hll4Array();
diff --git a/hll/include/Hll6Array-internal.hpp b/hll/include/Hll6Array-internal.hpp
index a318564..e9f6e9f 100644
--- a/hll/include/Hll6Array-internal.hpp
+++ b/hll/include/Hll6Array-internal.hpp
@@ -27,40 +27,29 @@
 namespace datasketches {
 
 template<typename A>
-Hll6Array<A>::Hll6Array(const int lgConfigK, const bool startFullSize) :
-    HllArray<A>(lgConfigK, target_hll_type::HLL_6, startFullSize) {
-  const int numBytes = this->hll6ArrBytes(lgConfigK);
-  typedef typename std::allocator_traits<A>::template rebind_alloc<uint8_t> uint8Alloc;
-  this->hllByteArr = uint8Alloc().allocate(numBytes);
-  std::fill(this->hllByteArr, this->hllByteArr + numBytes, 0);
-}
-
-template<typename A>
-Hll6Array<A>::Hll6Array(const Hll6Array<A>& that) :
-  HllArray<A>(that)
+Hll6Array<A>::Hll6Array(const int lgConfigK, const bool startFullSize, const A& allocator):
+HllArray<A>(lgConfigK, target_hll_type::HLL_6, startFullSize, allocator)
 {
-  // can determine hllByteArr size in parent class, no need to allocate here
-}
-
-template<typename A>
-Hll6Array<A>::~Hll6Array() {
-  // hllByteArr deleted in parent
+  const int numBytes = this->hll6ArrBytes(lgConfigK);
+  this->hllByteArr.resize(numBytes, 0);
 }
 
 template<typename A>
 std::function<void(HllSketchImpl<A>*)> Hll6Array<A>::get_deleter() const {
   return [](HllSketchImpl<A>* ptr) {
-    typedef typename std::allocator_traits<A>::template rebind_alloc<Hll6Array<A>> hll6Alloc;
+    using Hll6Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll6Array<A>>;
     Hll6Array<A>* hll = static_cast<Hll6Array<A>*>(ptr);
+    Hll6Alloc hll6Alloc(hll->getAllocator());
     hll->~Hll6Array();
-    hll6Alloc().deallocate(hll, 1);
+    hll6Alloc.deallocate(hll, 1);
   };
 }
 
 template<typename A>
 Hll6Array<A>* Hll6Array<A>::copy() const {
-  typedef typename std::allocator_traits<A>::template rebind_alloc<Hll6Array<A>> hll6Alloc;
-  return new (hll6Alloc().allocate(1)) Hll6Array<A>(*this);
+  using Hll6Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll6Array<A>>;
+  Hll6Alloc hll6Alloc(this->getAllocator());
+  return new (hll6Alloc.allocate(1)) Hll6Array<A>(*this);
 }
 
 template<typename A>
diff --git a/hll/include/Hll6Array.hpp b/hll/include/Hll6Array.hpp
index 5178de8..03370b2 100644
--- a/hll/include/Hll6Array.hpp
+++ b/hll/include/Hll6Array.hpp
@@ -30,10 +30,9 @@
 template<typename A>
 class Hll6Array final : public HllArray<A> {
   public:
-    explicit Hll6Array(int lgConfigK, bool startFullSize);
-    explicit Hll6Array(const Hll6Array<A>& that);
+    Hll6Array(int lgConfigK, bool startFullSize, const A& allocator);
 
-    virtual ~Hll6Array();
+    virtual ~Hll6Array() = default;
     virtual std::function<void(HllSketchImpl<A>*)> get_deleter() const;
 
     virtual Hll6Array* copy() const;
diff --git a/hll/include/Hll8Array-internal.hpp b/hll/include/Hll8Array-internal.hpp
index cb14a0f..f27a796 100644
--- a/hll/include/Hll8Array-internal.hpp
+++ b/hll/include/Hll8Array-internal.hpp
@@ -25,40 +25,29 @@
 namespace datasketches {
 
 template<typename A>
-Hll8Array<A>::Hll8Array(const int lgConfigK, const bool startFullSize) :
-    HllArray<A>(lgConfigK, target_hll_type::HLL_8, startFullSize) {
-  const int numBytes = this->hll8ArrBytes(lgConfigK);
-  typedef typename std::allocator_traits<A>::template rebind_alloc<uint8_t> uint8Alloc;
-  this->hllByteArr = uint8Alloc().allocate(numBytes);
-  std::fill(this->hllByteArr, this->hllByteArr + numBytes, 0);
-}
-
-template<typename A>
-Hll8Array<A>::Hll8Array(const Hll8Array<A>& that) :
-  HllArray<A>(that)
+Hll8Array<A>::Hll8Array(const int lgConfigK, const bool startFullSize, const A& allocator):
+HllArray<A>(lgConfigK, target_hll_type::HLL_8, startFullSize, allocator)
 {
-  // can determine hllByteArr size in parent class, no need to allocate here
-}
-
-template<typename A>
-Hll8Array<A>::~Hll8Array() {
-  // hllByteArr deleted in parent
+  const int numBytes = this->hll8ArrBytes(lgConfigK);
+  this->hllByteArr.resize(numBytes, 0);
 }
 
 template<typename A>
 std::function<void(HllSketchImpl<A>*)> Hll8Array<A>::get_deleter() const {
   return [](HllSketchImpl<A>* ptr) {
-    typedef typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>> hll8Alloc;
     Hll8Array<A>* hll = static_cast<Hll8Array<A>*>(ptr);
+    using Hll8Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>>;
+    Hll8Alloc hll8Alloc(hll->getAllocator());
     hll->~Hll8Array();
-    hll8Alloc().deallocate(hll, 1);
+    hll8Alloc.deallocate(hll, 1);
   };
 }
 
 template<typename A>
 Hll8Array<A>* Hll8Array<A>::copy() const {
-  typedef typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>> hll8Alloc;
-  return new (hll8Alloc().allocate(1)) Hll8Array<A>(*this);
+  using Hll8Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>>;
+  Hll8Alloc hll8Alloc(this->getAllocator());
+  return new (hll8Alloc.allocate(1)) Hll8Array<A>(*this);
 }
 
 template<typename A>
diff --git a/hll/include/Hll8Array.hpp b/hll/include/Hll8Array.hpp
index 2b0aefc..ea9a5bd 100644
--- a/hll/include/Hll8Array.hpp
+++ b/hll/include/Hll8Array.hpp
@@ -30,10 +30,9 @@
 template<typename A>
 class Hll8Array final : public HllArray<A> {
   public:
-    explicit Hll8Array(int lgConfigK, bool startFullSize);
-    explicit Hll8Array(const Hll8Array& that);
+    Hll8Array(int lgConfigK, bool startFullSize, const A& allocator);
 
-    virtual ~Hll8Array();
+    virtual ~Hll8Array() = default;
     virtual std::function<void(HllSketchImpl<A>*)> get_deleter() const;
 
     virtual Hll8Array<A>* copy() const;
diff --git a/hll/include/HllArray-internal.hpp b/hll/include/HllArray-internal.hpp
index 0a4bdce..4479417 100644
--- a/hll/include/HllArray-internal.hpp
+++ b/hll/include/HllArray-internal.hpp
@@ -35,48 +35,16 @@
 namespace datasketches {
 
 template<typename A>
-HllArray<A>::HllArray(const int lgConfigK, const target_hll_type tgtHllType, bool startFullSize)
-  : HllSketchImpl<A>(lgConfigK, tgtHllType, hll_mode::HLL, startFullSize) {
-  hipAccum = 0.0;
-  kxq0 = 1 << lgConfigK;
-  kxq1 = 0.0;
-  curMin = 0;
-  numAtCurMin = 1 << lgConfigK;
-  oooFlag = false;
-  hllByteArr = nullptr; // allocated in derived class
-}
-
-template<typename A>
-HllArray<A>::HllArray(const HllArray<A>& that):
-HllSketchImpl<A>(that.lgConfigK, that.tgtHllType, hll_mode::HLL, that.startFullSize),
-hipAccum(that.hipAccum),
-kxq0(that.kxq0),
-kxq1(that.kxq1),
-hllByteArr(nullptr),
-curMin(that.curMin),
-numAtCurMin(that.numAtCurMin),
-oooFlag(that.oooFlag)
-{
-  const int arrayLen = that.getHllByteArrBytes();
-  typedef typename std::allocator_traits<A>::template rebind_alloc<uint8_t> uint8Alloc;
-  hllByteArr = uint8Alloc().allocate(arrayLen);
-  std::copy(that.hllByteArr, that.hllByteArr + arrayLen, hllByteArr);
-}
-
-template<typename A>
-HllArray<A>::~HllArray() {
-  // need to determine number of bytes to deallocate
-  int hllArrBytes = 0;
-  if (this->tgtHllType == target_hll_type::HLL_4) {
-    hllArrBytes = hll4ArrBytes(this->lgConfigK);
-  } else if (this->tgtHllType == target_hll_type::HLL_6) {
-    hllArrBytes = hll6ArrBytes(this->lgConfigK);
-  } else { // tgtHllType == HLL_8
-    hllArrBytes = hll8ArrBytes(this->lgConfigK);
-  }
-  typedef typename std::allocator_traits<A>::template rebind_alloc<uint8_t> uint8Alloc;
-  uint8Alloc().deallocate(hllByteArr, hllArrBytes);
-}
+HllArray<A>::HllArray(const int lgConfigK, const target_hll_type tgtHllType, bool startFullSize, const A& allocator):
+HllSketchImpl<A>(lgConfigK, tgtHllType, hll_mode::HLL, startFullSize),
+hipAccum(0.0),
+kxq0(1 << lgConfigK),
+kxq1(0.0),
+hllByteArr(allocator),
+curMin(0),
+numAtCurMin(1 << lgConfigK),
+oooFlag(false)
+{}
 
 template<typename A>
 HllArray<A>* HllArray<A>::copyAs(const target_hll_type tgtHllType) const {
@@ -93,7 +61,7 @@
 }
 
 template<typename A>
-HllArray<A>* HllArray<A>::newHll(const void* bytes, size_t len) {
+HllArray<A>* HllArray<A>::newHll(const void* bytes, size_t len, const A& allocator) {
   if (len < HllUtil<A>::HLL_BYTE_ARR_START) {
     throw std::out_of_range("Input data length insufficient to hold HLL array");
   }
@@ -143,11 +111,11 @@
     int auxLgIntArrSize = (int) data[4];
     const size_t offset = HllUtil<A>::HLL_BYTE_ARR_START + arrayBytes;
     const uint8_t* auxDataStart = data + offset;
-    auxHashMap = AuxHashMap<A>::deserialize(auxDataStart, len - offset, lgK, auxCount, auxLgIntArrSize, comapctFlag);
+    auxHashMap = AuxHashMap<A>::deserialize(auxDataStart, len - offset, lgK, auxCount, auxLgIntArrSize, comapctFlag, allocator);
     aux_ptr = aux_hash_map_ptr(auxHashMap, auxHashMap->make_deleter());
   }
 
-  HllArray<A>* sketch = HllSketchImplFactory<A>::newHll(lgK, tgtHllType, startFullSizeFlag);
+  HllArray<A>* sketch = HllSketchImplFactory<A>::newHll(lgK, tgtHllType, startFullSizeFlag, allocator);
   sketch->putCurMin(curMin);
   sketch->putOutOfOrderFlag(oooFlag);
   if (!oooFlag) sketch->putHipAccum(hip);
@@ -155,7 +123,7 @@
   sketch->putKxQ1(kxq1);
   sketch->putNumAtCurMin(numAtCurMin);
 
-  std::memcpy(sketch->hllByteArr, data + HllUtil<A>::HLL_BYTE_ARR_START, arrayBytes);
+  std::memcpy(sketch->hllByteArr.data(), data + HllUtil<A>::HLL_BYTE_ARR_START, arrayBytes);
 
   if (auxHashMap != nullptr)
     ((Hll4Array<A>*)sketch)->putAuxHashMap(auxHashMap);
@@ -165,7 +133,7 @@
 }
 
 template<typename A>
-HllArray<A>* HllArray<A>::newHll(std::istream& is) {
+HllArray<A>* HllArray<A>::newHll(std::istream& is, const A& allocator) {
   uint8_t listHeader[8];
   is.read((char*)listHeader, 8 * sizeof(uint8_t));
 
@@ -192,7 +160,7 @@
   const int lgK = (int) listHeader[HllUtil<A>::LG_K_BYTE];
   const int curMin = (int) listHeader[HllUtil<A>::HLL_CUR_MIN_BYTE];
 
-  HllArray* sketch = HllSketchImplFactory<A>::newHll(lgK, tgtHllType, startFullSizeFlag);
+  HllArray* sketch = HllSketchImplFactory<A>::newHll(lgK, tgtHllType, startFullSizeFlag, allocator);
   typedef std::unique_ptr<HllArray<A>, std::function<void(HllSketchImpl<A>*)>> hll_array_ptr;
   hll_array_ptr sketch_ptr(sketch, sketch->get_deleter());
   sketch->putCurMin(curMin);
@@ -211,11 +179,11 @@
   is.read((char*)&auxCount, sizeof(auxCount));
   sketch->putNumAtCurMin(numAtCurMin);
   
-  is.read((char*)sketch->hllByteArr, sketch->getHllByteArrBytes());
+  is.read((char*)sketch->hllByteArr.data(), sketch->getHllByteArrBytes());
   
   if (auxCount > 0) { // necessarily TgtHllType == HLL_4
     int auxLgIntArrSize = listHeader[4];
-    AuxHashMap<A>* auxHashMap = AuxHashMap<A>::deserialize(is, lgK, auxCount, auxLgIntArrSize, comapctFlag);
+    AuxHashMap<A>* auxHashMap = AuxHashMap<A>::deserialize(is, lgK, auxCount, auxLgIntArrSize, comapctFlag, allocator);
     ((Hll4Array<A>*)sketch)->putAuxHashMap(auxHashMap);
   }
 
@@ -228,7 +196,7 @@
 template<typename A>
 vector_u8<A> HllArray<A>::serialize(bool compact, unsigned header_size_bytes) const {
   const size_t sketchSizeBytes = (compact ? getCompactSerializationBytes() : getUpdatableSerializationBytes()) + header_size_bytes;
-  vector_u8<A> byteArr(sketchSizeBytes);
+  vector_u8<A> byteArr(sketchSizeBytes, 0, getAllocator());
   uint8_t* bytes = byteArr.data() + header_size_bytes;
   AuxHashMap<A>* auxHashMap = getAuxHashMap();
 
@@ -249,7 +217,7 @@
   std::memcpy(bytes + HllUtil<A>::AUX_COUNT_INT, &auxCount, sizeof(int));
 
   const int hllByteArrBytes = getHllByteArrBytes();
-  std::memcpy(bytes + getMemDataStart(), hllByteArr, hllByteArrBytes);
+  std::memcpy(bytes + getMemDataStart(), hllByteArr.data(), hllByteArrBytes);
 
   // aux map if HLL_4
   if (this->tgtHllType == HLL_4) {
@@ -309,7 +277,7 @@
 
   const int auxCount = (auxHashMap == nullptr ? 0 : auxHashMap->getAuxCount());
   os.write((char*)&auxCount, sizeof(auxCount));
-  os.write((char*)hllByteArr, getHllByteArrBytes());
+  os.write((char*)hllByteArr.data(), getHllByteArrBytes());
 
   // aux map if HLL_4
   if (this->tgtHllType == HLL_4) {
@@ -639,12 +607,12 @@
 
 template<typename A>
 typename HllArray<A>::const_iterator HllArray<A>::begin(bool all) const {
-  return const_iterator(hllByteArr, 1 << this->lgConfigK, 0, this->tgtHllType, nullptr, 0, all);
+  return const_iterator(hllByteArr.data(), 1 << this->lgConfigK, 0, this->tgtHllType, nullptr, 0, all);
 }
 
 template<typename A>
 typename HllArray<A>::const_iterator HllArray<A>::end() const {
-  return const_iterator(hllByteArr, 1 << this->lgConfigK, 1 << this->lgConfigK, this->tgtHllType, nullptr, 0, false);
+  return const_iterator(hllByteArr.data(), 1 << this->lgConfigK, 1 << this->lgConfigK, this->tgtHllType, nullptr, 0, false);
 }
 
 template<typename A>
@@ -701,6 +669,11 @@
   return array[index];
 }
 
+template<typename A>
+A HllArray<A>::getAllocator() const {
+  return hllByteArr.get_allocator();
+}
+
 }
 
 #endif // _HLLARRAY_INTERNAL_HPP_
diff --git a/hll/include/HllArray.hpp b/hll/include/HllArray.hpp
index 1cc64ea..e7be8c1 100644
--- a/hll/include/HllArray.hpp
+++ b/hll/include/HllArray.hpp
@@ -28,19 +28,18 @@
 template<typename A>
 class AuxHashMap;
 
-template<typename A = std::allocator<char>>
+template<typename A>
 class HllArray : public HllSketchImpl<A> {
   public:
-    explicit HllArray(int lgConfigK, target_hll_type tgtHllType, bool startFullSize);
-    explicit HllArray(const HllArray<A>& that);
+    HllArray(int lgConfigK, target_hll_type tgtHllType, bool startFullSize, const A& allocator);
 
-    static HllArray* newHll(const void* bytes, size_t len);
-    static HllArray* newHll(std::istream& is);
+    static HllArray* newHll(const void* bytes, size_t len, const A& allocator);
+    static HllArray* newHll(std::istream& is, const A& allocator);
 
     virtual vector_u8<A> serialize(bool compact, unsigned header_size_bytes) const;
     virtual void serialize(std::ostream& os, bool compact) const;
 
-    virtual ~HllArray();
+    virtual ~HllArray() = default;
     virtual std::function<void(HllSketchImpl<A>*)> get_deleter() const = 0;
 
     virtual HllArray* copy() const = 0;
@@ -95,6 +94,8 @@
     virtual const_iterator begin(bool all = false) const;
     virtual const_iterator end() const;
 
+    virtual A getAllocator() const;
+
   protected:
     void hipAndKxQIncrementalUpdate(uint8_t oldValue, uint8_t newValue);
     double getHllBitMapEstimate(int lgConfigK, int curMin, int numAtCurMin) const;
@@ -103,7 +104,7 @@
     double hipAccum;
     double kxq0;
     double kxq1;
-    uint8_t* hllByteArr; //init by sub-classes
+    vector_u8<A> hllByteArr; //init by sub-classes
     int curMin; //always zero for Hll6 and Hll8, only tracked by Hll4Array
     int numAtCurMin; //interpreted as num zeros when curMin == 0
     bool oooFlag; //Out-Of-Order Flag
@@ -115,7 +116,6 @@
 class HllArray<A>::const_iterator: public std::iterator<std::input_iterator_tag, uint32_t> {
 public:
   const_iterator(const uint8_t* array, size_t array_slze, size_t index, target_hll_type hll_type, const AuxHashMap<A>* exceptions, uint8_t offset, bool all);
-  //const_iterator(const uint8_t* array, size_t array_slze, size_t index, target_hll_type hll_type, const AuxHashMap<A>* exceptions, uint8_t offset);
   const_iterator& operator++();
   bool operator!=(const const_iterator& other) const;
   uint32_t operator*() const;
diff --git a/hll/include/HllSketch-internal.hpp b/hll/include/HllSketch-internal.hpp
index dd16955..8f7d1f4 100644
--- a/hll/include/HllSketch-internal.hpp
+++ b/hll/include/HllSketch-internal.hpp
@@ -42,28 +42,26 @@
 } longDoubleUnion;
 
 template<typename A>
-hll_sketch_alloc<A>::hll_sketch_alloc(int lg_config_k, target_hll_type tgt_type, bool start_full_size) {
+hll_sketch_alloc<A>::hll_sketch_alloc(int lg_config_k, target_hll_type tgt_type, bool start_full_size, const A& allocator) {
   HllUtil<A>::checkLgK(lg_config_k);
   if (start_full_size) {
-    sketch_impl = HllSketchImplFactory<A>::newHll(lg_config_k, tgt_type, start_full_size);
+    sketch_impl = HllSketchImplFactory<A>::newHll(lg_config_k, tgt_type, start_full_size, allocator);
   } else {
     typedef typename std::allocator_traits<A>::template rebind_alloc<CouponList<A>> clAlloc;
-    sketch_impl = new (clAlloc().allocate(1)) CouponList<A>(lg_config_k, tgt_type, hll_mode::LIST);
+    sketch_impl = new (clAlloc(allocator).allocate(1)) CouponList<A>(lg_config_k, tgt_type, hll_mode::LIST, allocator);
   }
 }
 
 template<typename A>
-hll_sketch_alloc<A> hll_sketch_alloc<A>::deserialize(std::istream& is) {
-  HllSketchImpl<A>* impl = HllSketchImplFactory<A>::deserialize(is);
-  hll_sketch_alloc<A> sketch(impl);
-  return sketch;
+hll_sketch_alloc<A> hll_sketch_alloc<A>::deserialize(std::istream& is, const A& allocator) {
+  HllSketchImpl<A>* impl = HllSketchImplFactory<A>::deserialize(is, allocator);
+  return hll_sketch_alloc<A>(impl);
 }
 
 template<typename A>
-hll_sketch_alloc<A> hll_sketch_alloc<A>::deserialize(const void* bytes, size_t len) {
-  HllSketchImpl<A>* impl = HllSketchImplFactory<A>::deserialize(bytes, len);
-  hll_sketch_alloc<A> sketch(impl);
-  return sketch;
+hll_sketch_alloc<A> hll_sketch_alloc<A>::deserialize(const void* bytes, size_t len, const A& allocator) {
+  HllSketchImpl<A>* impl = HllSketchImplFactory<A>::deserialize(bytes, len, allocator);
+  return hll_sketch_alloc<A>(impl);
 }
 
 template<typename A>
diff --git a/hll/include/HllSketchImpl.hpp b/hll/include/HllSketchImpl.hpp
index 82180b4..9f53705 100644
--- a/hll/include/HllSketchImpl.hpp
+++ b/hll/include/HllSketchImpl.hpp
@@ -27,7 +27,7 @@
 
 namespace datasketches {
 
-template<typename A = std::allocator<char>>
+template<typename A>
 class HllSketchImpl {
   public:
     HllSketchImpl(int lgConfigK, target_hll_type tgtHllType, hll_mode mode, bool startFullSize);
@@ -66,6 +66,7 @@
     virtual bool isEmpty() const = 0;
     virtual bool isOutOfOrderFlag() const = 0;
     virtual void putOutOfOrderFlag(bool oooFlag) = 0;
+    virtual A getAllocator() const = 0;
     bool isStartFullSize() const;
 
   protected:
diff --git a/hll/include/HllSketchImplFactory.hpp b/hll/include/HllSketchImplFactory.hpp
index eb8dd77..85f9618 100644
--- a/hll/include/HllSketchImplFactory.hpp
+++ b/hll/include/HllSketchImplFactory.hpp
@@ -31,15 +31,15 @@
 
 namespace datasketches {
 
-template<typename A = std::allocator<char>>
+template<typename A>
 class HllSketchImplFactory final {
 public:
-  static HllSketchImpl<A>* deserialize(std::istream& os);
-  static HllSketchImpl<A>* deserialize(const void* bytes, size_t len);
+  static HllSketchImpl<A>* deserialize(std::istream& os, const A& allocator);
+  static HllSketchImpl<A>* deserialize(const void* bytes, size_t len, const A& allocator);
 
   static CouponHashSet<A>* promoteListToSet(const CouponList<A>& list);
   static HllArray<A>* promoteListOrSetToHll(const CouponList<A>& list);
-  static HllArray<A>* newHll(int lgConfigK, target_hll_type tgtHllType, bool startFullSize = false);
+  static HllArray<A>* newHll(int lgConfigK, target_hll_type tgtHllType, bool startFullSize, const A& allocator);
   
   // resets the input impl, deleting the input pointer and returning a new pointer
   static HllSketchImpl<A>* reset(HllSketchImpl<A>* impl, bool startFullSize);
@@ -51,8 +51,8 @@
 
 template<typename A>
 CouponHashSet<A>* HllSketchImplFactory<A>::promoteListToSet(const CouponList<A>& list) {
-  typedef typename std::allocator_traits<A>::template rebind_alloc<CouponHashSet<A>> chsAlloc;
-  CouponHashSet<A>* chSet = new (chsAlloc().allocate(1)) CouponHashSet<A>(list.getLgConfigK(), list.getTgtHllType());
+  using ChsAlloc = typename std::allocator_traits<A>::template rebind_alloc<CouponHashSet<A>>;
+  CouponHashSet<A>* chSet = new (ChsAlloc(list.getAllocator()).allocate(1)) CouponHashSet<A>(list.getLgConfigK(), list.getTgtHllType(), list.getAllocator());
   for (auto coupon: list) {
     chSet->couponUpdate(coupon);
   }
@@ -61,7 +61,7 @@
 
 template<typename A>
 HllArray<A>* HllSketchImplFactory<A>::promoteListOrSetToHll(const CouponList<A>& src) {
-  HllArray<A>* tgtHllArr = HllSketchImplFactory<A>::newHll(src.getLgConfigK(), src.getTgtHllType());
+  HllArray<A>* tgtHllArr = HllSketchImplFactory<A>::newHll(src.getLgConfigK(), src.getTgtHllType(), false, src.getAllocator());
   tgtHllArr->putKxQ0(1 << src.getLgConfigK());
   for (auto coupon: src) {
     tgtHllArr->couponUpdate(coupon);
@@ -72,48 +72,48 @@
 }
 
 template<typename A>
-HllSketchImpl<A>* HllSketchImplFactory<A>::deserialize(std::istream& is) {
+HllSketchImpl<A>* HllSketchImplFactory<A>::deserialize(std::istream& is, const A& allocator) {
   // we'll hand off the sketch based on PreInts so we don't need
   // to move the stream pointer back and forth -- perhaps somewhat fragile?
   const int preInts = is.peek();
   if (preInts == HllUtil<A>::HLL_PREINTS) {
-    return HllArray<A>::newHll(is);
+    return HllArray<A>::newHll(is, allocator);
   } else if (preInts == HllUtil<A>::HASH_SET_PREINTS) {
-    return CouponHashSet<A>::newSet(is);
+    return CouponHashSet<A>::newSet(is, allocator);
   } else if (preInts == HllUtil<A>::LIST_PREINTS) {
-    return CouponList<A>::newList(is);
+    return CouponList<A>::newList(is, allocator);
   } else {
     throw std::invalid_argument("Attempt to deserialize unknown object type");
   }
 }
 
 template<typename A>
-HllSketchImpl<A>* HllSketchImplFactory<A>::deserialize(const void* bytes, size_t len) {
+HllSketchImpl<A>* HllSketchImplFactory<A>::deserialize(const void* bytes, size_t len, const A& allocator) {
   // read current mode directly
   const int preInts = static_cast<const uint8_t*>(bytes)[0];
   if (preInts == HllUtil<A>::HLL_PREINTS) {
-    return HllArray<A>::newHll(bytes, len);
+    return HllArray<A>::newHll(bytes, len, allocator);
   } else if (preInts == HllUtil<A>::HASH_SET_PREINTS) {
-    return CouponHashSet<A>::newSet(bytes, len);
+    return CouponHashSet<A>::newSet(bytes, len, allocator);
   } else if (preInts == HllUtil<A>::LIST_PREINTS) {
-    return CouponList<A>::newList(bytes, len);
+    return CouponList<A>::newList(bytes, len, allocator);
   } else {
     throw std::invalid_argument("Attempt to deserialize unknown object type");
   }
 }
 
 template<typename A>
-HllArray<A>* HllSketchImplFactory<A>::newHll(int lgConfigK, target_hll_type tgtHllType, bool startFullSize) {
+HllArray<A>* HllSketchImplFactory<A>::newHll(int lgConfigK, target_hll_type tgtHllType, bool startFullSize, const A& allocator) {
   switch (tgtHllType) {
     case HLL_8:
-      typedef typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>> hll8Alloc;
-      return new (hll8Alloc().allocate(1)) Hll8Array<A>(lgConfigK, startFullSize);
+      using Hll8Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>>;
+      return new (Hll8Alloc(allocator).allocate(1)) Hll8Array<A>(lgConfigK, startFullSize, allocator);
     case HLL_6:
-      typedef typename std::allocator_traits<A>::template rebind_alloc<Hll6Array<A>> hll6Alloc;
-      return new (hll6Alloc().allocate(1)) Hll6Array<A>(lgConfigK, startFullSize);
+      using Hll6Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll6Array<A>>;
+      return new (Hll6Alloc(allocator).allocate(1)) Hll6Array<A>(lgConfigK, startFullSize, allocator);
     case HLL_4:
-      typedef typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>> hll4Alloc;
-      return new (hll4Alloc().allocate(1)) Hll4Array<A>(lgConfigK, startFullSize);
+      using Hll4Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>>;
+      return new (Hll4Alloc(allocator).allocate(1)) Hll4Array<A>(lgConfigK, startFullSize, allocator);
   }
   throw std::logic_error("Invalid target_hll_type");
 }
@@ -121,12 +121,12 @@
 template<typename A>
 HllSketchImpl<A>* HllSketchImplFactory<A>::reset(HllSketchImpl<A>* impl, bool startFullSize) {
   if (startFullSize) {
-    HllArray<A>* hll = newHll(impl->getLgConfigK(), impl->getTgtHllType(), startFullSize);
+    HllArray<A>* hll = newHll(impl->getLgConfigK(), impl->getTgtHllType(), startFullSize, impl->getAllocator());
     impl->get_deleter()(impl);
     return hll;
   } else {
-    typedef typename std::allocator_traits<A>::template rebind_alloc<CouponList<A>> clAlloc;
-    CouponList<A>* cl = new (clAlloc().allocate(1)) CouponList<A>(impl->getLgConfigK(), impl->getTgtHllType(), hll_mode::LIST);
+    using ClAlloc = typename std::allocator_traits<A>::template rebind_alloc<CouponList<A>>;
+    CouponList<A>* cl = new (ClAlloc(impl->getAllocator()).allocate(1)) CouponList<A>(impl->getLgConfigK(), impl->getTgtHllType(), hll_mode::LIST, impl->getAllocator());
     impl->get_deleter()(impl);
     return cl;
   }
@@ -135,8 +135,9 @@
 template<typename A>
 Hll4Array<A>* HllSketchImplFactory<A>::convertToHll4(const HllArray<A>& srcHllArr) {
   const int lgConfigK = srcHllArr.getLgConfigK();
-  typedef typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>> hll4Alloc;
-  Hll4Array<A>* hll4Array = new (hll4Alloc().allocate(1)) Hll4Array<A>(lgConfigK, srcHllArr.isStartFullSize());
+  using Hll4Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll4Array<A>>;
+  Hll4Array<A>* hll4Array = new (Hll4Alloc(srcHllArr.getAllocator()).allocate(1))
+      Hll4Array<A>(lgConfigK, srcHllArr.isStartFullSize(), srcHllArr.getAllocator());
   hll4Array->putOutOfOrderFlag(srcHllArr.isOutOfOrderFlag());
   hll4Array->mergeHll(srcHllArr);
   hll4Array->putHipAccum(srcHllArr.getHipAccum());
@@ -146,8 +147,9 @@
 template<typename A>
 Hll6Array<A>* HllSketchImplFactory<A>::convertToHll6(const HllArray<A>& srcHllArr) {
   const int lgConfigK = srcHllArr.getLgConfigK();
-  typedef typename std::allocator_traits<A>::template rebind_alloc<Hll6Array<A>> hll6Alloc;
-  Hll6Array<A>* hll6Array = new (hll6Alloc().allocate(1)) Hll6Array<A>(lgConfigK, srcHllArr.isStartFullSize());
+  using Hll6Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll6Array<A>>;
+  Hll6Array<A>* hll6Array = new (Hll6Alloc(srcHllArr.getAllocator()).allocate(1))
+      Hll6Array<A>(lgConfigK, srcHllArr.isStartFullSize(), srcHllArr.getAllocator());
   hll6Array->putOutOfOrderFlag(srcHllArr.isOutOfOrderFlag());
   hll6Array->mergeHll(srcHllArr);
   hll6Array->putHipAccum(srcHllArr.getHipAccum());
@@ -157,8 +159,9 @@
 template<typename A>
 Hll8Array<A>* HllSketchImplFactory<A>::convertToHll8(const HllArray<A>& srcHllArr) {
   const int lgConfigK = srcHllArr.getLgConfigK();
-  typedef typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>> hll8Alloc;
-  Hll8Array<A>* hll8Array = new (hll8Alloc().allocate(1)) Hll8Array<A>(lgConfigK, srcHllArr.isStartFullSize());
+  using Hll8Alloc = typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>>;
+  Hll8Array<A>* hll8Array = new (Hll8Alloc(srcHllArr.getAllocator()).allocate(1))
+      Hll8Array<A>(lgConfigK, srcHllArr.isStartFullSize(), srcHllArr.getAllocator());
   hll8Array->putOutOfOrderFlag(srcHllArr.isOutOfOrderFlag());
   hll8Array->mergeHll(srcHllArr);
   hll8Array->putHipAccum(srcHllArr.getHipAccum());
diff --git a/hll/include/HllUnion-internal.hpp b/hll/include/HllUnion-internal.hpp
index 0d12fd3..716fab6 100644
--- a/hll/include/HllUnion-internal.hpp
+++ b/hll/include/HllUnion-internal.hpp
@@ -32,9 +32,9 @@
 namespace datasketches {
 
 template<typename A>
-hll_union_alloc<A>::hll_union_alloc(const int lg_max_k):
+hll_union_alloc<A>::hll_union_alloc(const int lg_max_k, const A& allocator):
   lg_max_k(HllUtil<A>::checkLgK(lg_max_k)),
-  gadget(lg_max_k, target_hll_type::HLL_8)
+  gadget(lg_max_k, target_hll_type::HLL_8, false, allocator)
 {}
 
 template<typename A>
@@ -150,16 +150,6 @@
 }
 
 template<typename A>
-int hll_union_alloc<A>::get_compact_serialization_bytes() const {
-  return gadget.get_compact_serialization_bytes();
-}
-
-template<typename A>
-int hll_union_alloc<A>::get_updatable_serialization_bytes() const {
-  return gadget.get_updatable_serialization_bytes();
-}
-
-template<typename A>
 int hll_union_alloc<A>::get_lg_config_k() const {
   return gadget.get_lg_config_k();
 }
@@ -170,11 +160,6 @@
 }
 
 template<typename A>
-bool hll_union_alloc<A>::is_compact() const {
-  return gadget.is_compact();
-}
-
-template<typename A>
 bool hll_union_alloc<A>::is_empty() const {
   return gadget.is_empty();
 }
@@ -195,21 +180,11 @@
 }
 
 template<typename A>
-int hll_union_alloc<A>::get_serialization_version() const {
-  return HllUtil<A>::SER_VER;
-}
-
-template<typename A>
 target_hll_type hll_union_alloc<A>::get_target_type() const {
   return target_hll_type::HLL_8;
 }
 
 template<typename A>
-int hll_union_alloc<A>::get_max_serialization_bytes(const int lg_k) {
-  return hll_sketch_alloc<A>::get_max_updatable_serialization_bytes(lg_k, target_hll_type::HLL_8);
-}
-
-template<typename A>
 double hll_union_alloc<A>::get_rel_err(const bool upper_bound, const bool unioned,
                            const int lg_config_k, const int num_std_dev) {
   return HllUtil<A>::getRelErr(upper_bound, unioned, lg_config_k, num_std_dev);
@@ -226,7 +201,7 @@
     return src->copyAs(HLL_8);
   }
   typedef typename std::allocator_traits<A>::template rebind_alloc<Hll8Array<A>> hll8Alloc;
-  Hll8Array<A>* tgtHllArr = new (hll8Alloc().allocate(1)) Hll8Array<A>(tgt_lg_k, false);
+  Hll8Array<A>* tgtHllArr = new (hll8Alloc(src->getAllocator()).allocate(1)) Hll8Array<A>(tgt_lg_k, false, src->getAllocator());
   tgtHllArr->mergeHll(*src);
   //both of these are required for isomorphism
   tgtHllArr->putHipAccum(src->getHipAccum());
diff --git a/hll/include/HllUtil.hpp b/hll/include/HllUtil.hpp
index ec0ddf2..3a1ebe2 100644
--- a/hll/include/HllUtil.hpp
+++ b/hll/include/HllUtil.hpp
@@ -36,7 +36,7 @@
 
 // template provides internal consistency and allows static float values
 // but we don't use the template parameter anywhere
-template<typename A = std::allocator<char> >
+template<typename A = std::allocator<uint8_t> >
 class HllUtil final {
 public:
   // preamble stuff
diff --git a/hll/include/RelativeErrorTables.hpp b/hll/include/RelativeErrorTables.hpp
index da8bebf..5e0a3c7 100644
--- a/hll/include/RelativeErrorTables.hpp
+++ b/hll/include/RelativeErrorTables.hpp
@@ -24,7 +24,7 @@
 
 namespace datasketches {
 
-template<typename A = std::allocator<char>>
+template<typename A = std::allocator<uint8_t>>
 class RelativeErrorTables {
   public:
     /**
diff --git a/hll/include/hll.hpp b/hll/include/hll.hpp
index 3898dda..a65b945 100644
--- a/hll/include/hll.hpp
+++ b/hll/include/hll.hpp
@@ -108,7 +108,7 @@
 template<typename A> using AllocU8 = typename std::allocator_traits<A>::template rebind_alloc<uint8_t>;
 template<typename A> using vector_u8 = std::vector<uint8_t, AllocU8<A>>;
 
-template<typename A = std::allocator<char> >
+template<typename A = std::allocator<uint8_t> >
 class hll_sketch_alloc final {
   public:
     /**
@@ -119,7 +119,7 @@
      *        keeping memory use constant (if HLL_6 or HLL_8) at the cost of
      *        starting out using much more memory
      */
-    explicit hll_sketch_alloc(int lg_config_k, target_hll_type tgt_type = HLL_4, bool start_full_size = false);
+    explicit hll_sketch_alloc(int lg_config_k, target_hll_type tgt_type = HLL_4, bool start_full_size = false, const A& allocator = A());
 
     /**
      * Copy constructor
@@ -140,14 +140,14 @@
      * Reconstructs a sketch from a serialized image on a stream.
      * @param is An input stream with a binary image of a sketch
      */
-    static hll_sketch_alloc deserialize(std::istream& is);
+    static hll_sketch_alloc deserialize(std::istream& is, const A& allocator = A());
 
     /**
      * Reconstructs a sketch from a serialized image in a byte array.
      * @param is bytes An input array with a binary image of a sketch
      * @param len Length of the input array, in bytes
      */
-    static hll_sketch_alloc deserialize(const void* bytes, size_t len);
+    static hll_sketch_alloc deserialize(const void* bytes, size_t len, const A& allocator = A());
 
     //! Class destructor
     virtual ~hll_sketch_alloc();
@@ -423,7 +423,7 @@
  * author Kevin Lang
  */
  
-template<typename A = std::allocator<char> >
+template<typename A = std::allocator<uint8_t> >
 class hll_union_alloc {
   public:
     /**
@@ -431,7 +431,7 @@
      * @param lg_max_k The maximum size, in log2, of k. The value must
      * be between 7 and 21, inclusive.
      */
-    explicit hll_union_alloc(int lg_max_k);
+    explicit hll_union_alloc(int lg_max_k, const A& allocator = A());
 
     /**
      * Returns the current cardinality estimate
@@ -469,18 +469,6 @@
     double get_upper_bound(int num_std_dev) const;
 
     /**
-     * Returns the size of the union serialized in compact form.
-     * @return Size of the union serialized in compact form, in bytes.
-     */
-    int get_compact_serialization_bytes() const;
-
-    /**
-     * Returns the size of the union serialized without compaction.
-     * @return Size of the union serialized without compaction, in bytes.
-     */
-    int get_updatable_serialization_bytes() const;
-
-    /**
      * Returns union's configured lg_k value.
      * @return Configured lg_k value.
      */
@@ -493,12 +481,6 @@
     target_hll_type get_target_type() const;
 
     /**
-     * Indicates if the union is currently stored compacted.
-     * @return True if the union is stored in compact form.
-     */
-    bool is_compact() const;
-
-    /**
      * Indicates if the union is currently empty.
      * @return True if the union is empty.
      */
@@ -606,15 +588,6 @@
     void update(const void* data, size_t length_bytes);
 
     /**
-     * Returns the maximum size in bytes that this union operator can grow to given a lg_k.
-     *
-     * @param lg_k The maximum Log2 of k for this union operator. This value must be
-     * between 4 and 21 inclusively.
-     * @return the maximum size in bytes that this union operator can grow to.
-     */
-    static int get_max_serialization_bytes(int lg_k);
-
-    /**
      * Gets the current (approximate) Relative Error (RE) asymptotic values given several
      * parameters. This is used primarily for testing.
      * @param upper_bound return the RE for the Upper Bound, otherwise for the Lower Bound.
@@ -645,7 +618,6 @@
     void coupon_update(int coupon);
 
     hll_mode get_current_mode() const;
-    int get_serialization_version() const;
     bool is_out_of_order_flag() const;
     bool is_estimation_mode() const;
 
diff --git a/hll/test/AuxHashMapTest.cpp b/hll/test/AuxHashMapTest.cpp
index 827f4ad..ed1b380 100644
--- a/hll/test/AuxHashMapTest.cpp
+++ b/hll/test/AuxHashMapTest.cpp
@@ -25,7 +25,7 @@
 namespace datasketches {
 
 TEST_CASE("aux hash map: check must replace", "[aux_hash_map]") {
-  AuxHashMap<>* map = new AuxHashMap<>(3, 7);
+  AuxHashMap<std::allocator<uint8_t>>* map = new AuxHashMap<std::allocator<uint8_t>>(3, 7, std::allocator<uint8_t>());
   map->mustAdd(100, 5);
   int val = map->mustFindValueFor(100);
   REQUIRE(val == 5);
@@ -40,9 +40,9 @@
 }
 
 TEST_CASE("aux hash map: check grow space", "[aux_hash_map]") {
-  auto map = std::unique_ptr<AuxHashMap<>, std::function<void(AuxHashMap<>*)>>(
-      AuxHashMap<>::newAuxHashMap(3, 7),
-      AuxHashMap<>::make_deleter()
+  auto map = std::unique_ptr<AuxHashMap<std::allocator<uint8_t>>, std::function<void(AuxHashMap<std::allocator<uint8_t>>*)>>(
+      AuxHashMap<std::allocator<uint8_t>>::newAuxHashMap(3, 7, std::allocator<uint8_t>()),
+      AuxHashMap<std::allocator<uint8_t>>::make_deleter()
       );
   REQUIRE(map->getLgAuxArrInts() == 3);
   for (int i = 1; i <= 7; ++i) {
@@ -63,17 +63,17 @@
 }
 
 TEST_CASE("aux hash map: check exception must find value for", "[aux_hash_map]") {
-  AuxHashMap<> map(3, 7);
+  AuxHashMap<std::allocator<uint8_t>> map(3, 7, std::allocator<uint8_t>());
   map.mustAdd(100, 5);
   REQUIRE_THROWS_AS(map.mustFindValueFor(101), std::invalid_argument);
 }
 
 TEST_CASE("aux hash map: check exception must add", "[aux_hash_map]") {
-  AuxHashMap<>* map = AuxHashMap<>::newAuxHashMap(3, 7);
+  AuxHashMap<std::allocator<uint8_t>>* map = AuxHashMap<std::allocator<uint8_t>>::newAuxHashMap(3, 7, std::allocator<uint8_t>());
   map->mustAdd(100, 5);
   REQUIRE_THROWS_AS(map->mustAdd(100, 6), std::invalid_argument);
   
-  AuxHashMap<>::make_deleter()(map);
+  AuxHashMap<std::allocator<uint8_t>>::make_deleter()(map);
 }
 
 } /* namespace datasketches */
diff --git a/hll/test/CouponHashSetTest.cpp b/hll/test/CouponHashSetTest.cpp
index 8e8b873..ebadd8f 100644
--- a/hll/test/CouponHashSetTest.cpp
+++ b/hll/test/CouponHashSetTest.cpp
@@ -43,7 +43,7 @@
   // fail in HllSketchImpl
   REQUIRE_THROWS_AS(hll_sketch::deserialize(bytes, size), std::invalid_argument);
   // fail in CouponHashSet
-  REQUIRE_THROWS_AS(CouponHashSet<>::newSet(bytes, size), std::invalid_argument);
+  REQUIRE_THROWS_AS(CouponHashSet<std::allocator<uint8_t>>::newSet(bytes, size, std::allocator<uint8_t>()), std::invalid_argument);
   bytes[HllUtil<>::PREAMBLE_INTS_BYTE] = HllUtil<>::HASH_SET_PREINTS;
 
   bytes[HllUtil<>::SER_VER_BYTE] = 0;
@@ -88,7 +88,7 @@
   // fail in HllSketchImpl
   REQUIRE_THROWS_AS(hll_sketch::deserialize(ss), std::invalid_argument);
   // fail in CouponHashSet
-  REQUIRE_THROWS_AS(CouponHashSet<>::newSet(ss), std::invalid_argument);
+  REQUIRE_THROWS_AS(CouponHashSet<std::allocator<uint8_t>>::newSet(ss, std::allocator<uint8_t>()), std::invalid_argument);
   ss.seekp(HllUtil<>::PREAMBLE_INTS_BYTE);
   ss.put(HllUtil<>::HASH_SET_PREINTS);
 
diff --git a/hll/test/CouponListTest.cpp b/hll/test/CouponListTest.cpp
index ac48d52..380b60d 100644
--- a/hll/test/CouponListTest.cpp
+++ b/hll/test/CouponListTest.cpp
@@ -36,7 +36,7 @@
 
 TEST_CASE("coupon list: check iterator", "[coupon_list]") {
   int lgConfigK = 8;
-  CouponList<> cl(lgConfigK, HLL_4, LIST);
+  CouponList<std::allocator<uint8_t>> cl(lgConfigK, HLL_4, LIST, std::allocator<uint8_t>());
   for (int i = 1; i <= 7; ++i) { cl.couponUpdate(HllUtil<>::pair(i, i)); } // not hashes but distinct values
   const int mask = (1 << lgConfigK) - 1;
   int idx = 0;
@@ -120,7 +120,7 @@
 
   bytes[HllUtil<>::PREAMBLE_INTS_BYTE] = 0;
   REQUIRE_THROWS_AS(hll_sketch::deserialize(bytes, size), std::invalid_argument);
-  REQUIRE_THROWS_AS(CouponList<>::newList(bytes, size), std::invalid_argument);
+  REQUIRE_THROWS_AS(CouponList<std::allocator<uint8_t>>::newList(bytes, size, std::allocator<uint8_t>()), std::invalid_argument);
 
   bytes[HllUtil<>::PREAMBLE_INTS_BYTE] = HllUtil<>::LIST_PREINTS;
 
@@ -154,7 +154,7 @@
   ss.put(0);
   ss.seekg(0);
   REQUIRE_THROWS_AS(hll_sketch::deserialize(ss), std::invalid_argument);
-  REQUIRE_THROWS_AS(CouponList<>::newList(ss), std::invalid_argument);
+  REQUIRE_THROWS_AS(CouponList<std::allocator<uint8_t>>::newList(ss, std::allocator<uint8_t>()), std::invalid_argument);
   ss.seekp(HllUtil<>::PREAMBLE_INTS_BYTE);
   ss.put(HllUtil<>::LIST_PREINTS);
 
diff --git a/hll/test/HllArrayTest.cpp b/hll/test/HllArrayTest.cpp
index 8c9efa2..f067dbb 100644
--- a/hll/test/HllArrayTest.cpp
+++ b/hll/test/HllArrayTest.cpp
@@ -111,7 +111,7 @@
 
   bytes[HllUtil<>::PREAMBLE_INTS_BYTE] = 0;
   REQUIRE_THROWS_AS(hll_sketch::deserialize(bytes, size), std::invalid_argument);
-  REQUIRE_THROWS_AS(HllArray<>::newHll(bytes, size), std::invalid_argument);
+  REQUIRE_THROWS_AS(HllArray<std::allocator<uint8_t>>::newHll(bytes, size, std::allocator<uint8_t>()), std::invalid_argument);
   bytes[HllUtil<>::PREAMBLE_INTS_BYTE] = HllUtil<>::HLL_PREINTS;
 
   bytes[HllUtil<>::SER_VER_BYTE] = 0;
@@ -150,7 +150,7 @@
   ss.put(0);
   ss.seekg(0);
   REQUIRE_THROWS_AS(hll_sketch::deserialize(ss), std::invalid_argument);
-  REQUIRE_THROWS_AS(HllArray<>::newHll(ss), std::invalid_argument);                                
+  REQUIRE_THROWS_AS(HllArray<std::allocator<uint8_t>>::newHll(ss, std::allocator<uint8_t>()), std::invalid_argument);
   ss.seekp(HllUtil<>::PREAMBLE_INTS_BYTE);
   ss.put(HllUtil<>::HLL_PREINTS);
 
diff --git a/hll/test/HllSketchTest.cpp b/hll/test/HllSketchTest.cpp
index b9c0429..dae2cab 100644
--- a/hll/test/HllSketchTest.cpp
+++ b/hll/test/HllSketchTest.cpp
@@ -24,7 +24,7 @@
 
 namespace datasketches {
 
-typedef hll_sketch_alloc<test_allocator<void>> hll_sketch_test_alloc;
+typedef hll_sketch_alloc<test_allocator<uint8_t>> hll_sketch_test_alloc;
 
 static void runCheckCopy(int lgConfigK, target_hll_type tgtHllType) {
   hll_sketch_test_alloc sk(lgConfigK, tgtHllType);
diff --git a/python/src/hll_wrapper.cpp b/python/src/hll_wrapper.cpp
index c4c97fe..de00be5 100644
--- a/python/src/hll_wrapper.cpp
+++ b/python/src/hll_wrapper.cpp
@@ -113,8 +113,6 @@
          "Returns the approximate lower error bound given the specified number of standard deviations in {1, 2, 3}")
     .def("get_upper_bound", &hll_union::get_upper_bound, py::arg("num_std_devs"),
          "Returns the approximate upper error bound given the specified number of standard deviations in {1, 2, 3}")
-    .def("is_compact", &hll_union::is_compact,
-         "True if the union is compact, otherwise False")
     .def("is_empty", &hll_union::is_empty,
          "True if the union is empty, otherwise False")    
     .def("reset", &hll_union::reset,
diff --git a/python/tests/hll_test.py b/python/tests/hll_test.py
index 4d7aa75..a7c085d 100644
--- a/python/tests/hll_test.py
+++ b/python/tests/hll_test.py
@@ -113,7 +113,6 @@
         self.assertGreaterEqual(union.get_upper_bound(1), union.get_estimate())
 
         self.assertEqual(union.lg_config_k, k)
-        self.assertFalse(union.is_compact())
         self.assertFalse(union.is_empty())
 
         sk = union.get_result()