more cleanup
diff --git a/hll/include/AuxHashMap-internal.hpp b/hll/include/AuxHashMap-internal.hpp
index 60142ec..dbc7f84 100644
--- a/hll/include/AuxHashMap-internal.hpp
+++ b/hll/include/AuxHashMap-internal.hpp
@@ -111,18 +111,16 @@
   int configKmask = (1 << lgConfigK) - 1;
 
   if (srcCompact) {
-    int pair;
     for (int i = 0; i < auxCount; ++i) {
-      is.read((char*)&pair, sizeof(pair));
+      const auto pair = read<int>(is);
       int slotNo = HllUtil<A>::getLow26(pair) & configKmask;
       int value = HllUtil<A>::getValue(pair);
       auxHashMap->mustAdd(slotNo, value);
     }
   } else { // updatable
     int itemsToRead = 1 << lgAuxArrInts;
-    int pair;
     for (int i = 0; i < itemsToRead; ++i) {
-      is.read((char*)&pair, sizeof(pair));
+      const auto pair = read<int>(is);
       if (pair == HllUtil<A>::EMPTY) { continue; }
       int slotNo = HllUtil<A>::getLow26(pair) & configKmask;
       int value = HllUtil<A>::getValue(pair);
diff --git a/hll/include/CouponHashSet-internal.hpp b/hll/include/CouponHashSet-internal.hpp
index 29a3ea7..67237db 100644
--- a/hll/include/CouponHashSet-internal.hpp
+++ b/hll/include/CouponHashSet-internal.hpp
@@ -125,7 +125,7 @@
 template<typename A>
 CouponHashSet<A>* CouponHashSet<A>::newSet(std::istream& is, const A& allocator) {
   uint8_t listHeader[8];
-  is.read((char*)listHeader, 8 * sizeof(uint8_t));
+  read(is, listHeader, 8 * sizeof(uint8_t));
 
   if (listHeader[HllUtil<A>::PREAMBLE_INTS_BYTE] != HllUtil<A>::HASH_SET_PREINTS) {
     throw std::invalid_argument("Incorrect number of preInts in input stream");
@@ -152,8 +152,7 @@
   int lgArrInts = listHeader[HllUtil<A>::LG_ARR_BYTE];
   const bool compactFlag = ((listHeader[HllUtil<A>::FLAGS_BYTE] & HllUtil<A>::COMPACT_FLAG_MASK) ? true : false);
 
-  int couponCount;
-  is.read((char*)&couponCount, sizeof(couponCount));
+  const auto couponCount = read<int>(is);
   if (lgArrInts < HllUtil<A>::LG_INIT_SET_SIZE) { 
     lgArrInts = HllUtil<A>::computeLgArrInts(SET, couponCount, lgK);
   }
@@ -167,15 +166,14 @@
   // we'll set later if updatable, and increment with updates if compact
   if (compactFlag) {
     for (int i = 0; i < couponCount; ++i) {
-      int coupon;
-      is.read((char*)&coupon, sizeof(coupon));
+      const auto coupon = read<int>(is);
       sketch->couponUpdate(coupon);
     }
   } else {
     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->coupons.data(), sketch->coupons.size() * sizeof(int));
+    read(is, sketch->coupons.data(), sketch->coupons.size() * sizeof(int));
   } 
 
   if (!is.good())
diff --git a/hll/include/CouponList-internal.hpp b/hll/include/CouponList-internal.hpp
index fd304c8..c48c7c2 100644
--- a/hll/include/CouponList-internal.hpp
+++ b/hll/include/CouponList-internal.hpp
@@ -121,7 +121,7 @@
 template<typename A>
 CouponList<A>* CouponList<A>::newList(std::istream& is, const A& allocator) {
   uint8_t listHeader[8];
-  is.read((char*)listHeader, 8 * sizeof(uint8_t));
+  read(is, listHeader, 8 * sizeof(uint8_t));
 
   if (listHeader[HllUtil<A>::PREAMBLE_INTS_BYTE] != HllUtil<A>::LIST_PREINTS) {
     throw std::invalid_argument("Incorrect number of preInts in input stream");
@@ -158,7 +158,7 @@
     // pointer ends up set correctly.
     // If not compact, still need to read empty items even though in order.
     const int numToRead = (compact ? couponCount : sketch->coupons.size());
-    is.read((char*)sketch->coupons.data(), numToRead * sizeof(int));
+    read(is, sketch->coupons.data(), numToRead * sizeof(int));
   }
 
   if (!is.good())
@@ -214,32 +214,32 @@
 void CouponList<A>::serialize(std::ostream& os, const bool compact) const {
   // header
   const uint8_t preInts(getPreInts());
-  os.write((char*)&preInts, sizeof(preInts));
+  write(os, preInts);
   const uint8_t serialVersion(HllUtil<A>::SER_VER);
-  os.write((char*)&serialVersion, sizeof(serialVersion));
+  write(os, serialVersion);
   const uint8_t familyId(HllUtil<A>::FAMILY_ID);
-  os.write((char*)&familyId, sizeof(familyId));
+  write(os, familyId);
   const uint8_t lgKByte((uint8_t) this->lgConfigK);
-  os.write((char*)&lgKByte, sizeof(lgKByte));
+  write(os, lgKByte);
   const uint8_t lgArrIntsByte(count_trailing_zeros_in_u32(coupons.size()));
-  os.write((char*)&lgArrIntsByte, sizeof(lgArrIntsByte));
+  write(os, lgArrIntsByte);
   const uint8_t flagsByte(this->makeFlagsByte(compact));
-  os.write((char*)&flagsByte, sizeof(flagsByte));
+  write(os, flagsByte);
 
   if (this->mode == LIST) {
     const uint8_t listCount((uint8_t) couponCount);
-    os.write((char*)&listCount, sizeof(listCount));
+    write(os, listCount);
   } else { // mode == SET
     const uint8_t unused(0);
-    os.write((char*)&unused, sizeof(unused));
+    write(os, unused);
   }
 
   const uint8_t modeByte(this->makeModeByte());
-  os.write((char*)&modeByte, sizeof(modeByte));
+  write(os, modeByte);
 
   if (this->mode == SET) {
     // writing as int, already stored as int
-    os.write((char*)&couponCount, sizeof(couponCount));
+    write(os, couponCount);
   }
 
   // coupons
@@ -247,12 +247,12 @@
   const int sw = (isCompact() ? 2 : 0) | (compact ? 1 : 0);
   switch (sw) {
     case 0: { // src updatable, dst updatable
-      os.write((char*)coupons.data(), coupons.size() * sizeof(int));
+      write(os, coupons.data(), coupons.size() * sizeof(int));
       break;
     }
     case 1: { // src updatable, dst compact
       for (uint32_t coupon: *this) {
-        os.write((char*)&coupon, sizeof(coupon));
+        write(os, coupon);
       }
       break;
     }
diff --git a/hll/include/HllArray-internal.hpp b/hll/include/HllArray-internal.hpp
index 4479417..95ef116 100644
--- a/hll/include/HllArray-internal.hpp
+++ b/hll/include/HllArray-internal.hpp
@@ -135,7 +135,7 @@
 template<typename A>
 HllArray<A>* HllArray<A>::newHll(std::istream& is, const A& allocator) {
   uint8_t listHeader[8];
-  is.read((char*)listHeader, 8 * sizeof(uint8_t));
+  read(is, listHeader, 8 * sizeof(uint8_t));
 
   if (listHeader[HllUtil<A>::PREAMBLE_INTS_BYTE] != HllUtil<A>::HLL_PREINTS) {
     throw std::invalid_argument("Incorrect number of preInts in input stream");
@@ -166,20 +166,18 @@
   sketch->putCurMin(curMin);
   sketch->putOutOfOrderFlag(oooFlag);
 
-  double hip, kxq0, kxq1;
-  is.read((char*)&hip, sizeof(hip));
-  is.read((char*)&kxq0, sizeof(kxq0));
-  is.read((char*)&kxq1, sizeof(kxq1));
+  const auto hip = read<double>(is);
+  const auto kxq0 = read<double>(is);
+  const auto kxq1 = read<double>(is);
   if (!oooFlag) sketch->putHipAccum(hip);
   sketch->putKxQ0(kxq0);
   sketch->putKxQ1(kxq1);
 
-  int numAtCurMin, auxCount;
-  is.read((char*)&numAtCurMin, sizeof(numAtCurMin));
-  is.read((char*)&auxCount, sizeof(auxCount));
+  const auto numAtCurMin = read<int>(is);
+  const auto auxCount = read<int>(is);
   sketch->putNumAtCurMin(numAtCurMin);
   
-  is.read((char*)sketch->hllByteArr.data(), sketch->getHllByteArrBytes());
+  read(is, sketch->hllByteArr.data(), sketch->getHllByteArrBytes());
   
   if (auxCount > 0) { // necessarily TgtHllType == HLL_4
     int auxLgIntArrSize = listHeader[4];
@@ -245,49 +243,49 @@
 void HllArray<A>::serialize(std::ostream& os, const bool compact) const {
   // header
   const uint8_t preInts(getPreInts());
-  os.write((char*)&preInts, sizeof(preInts));
+  write(os, preInts);
   const uint8_t serialVersion(HllUtil<A>::SER_VER);
-  os.write((char*)&serialVersion, sizeof(serialVersion));
+  write(os, serialVersion);
   const uint8_t familyId(HllUtil<A>::FAMILY_ID);
-  os.write((char*)&familyId, sizeof(familyId));
+  write(os, familyId);
   const uint8_t lgKByte((uint8_t) this->lgConfigK);
-  os.write((char*)&lgKByte, sizeof(lgKByte));
+  write(os, lgKByte);
 
   AuxHashMap<A>* auxHashMap = getAuxHashMap();
   uint8_t lgArrByte(0);
   if (auxHashMap != nullptr) {
     lgArrByte = auxHashMap->getLgAuxArrInts();
   }
-  os.write((char*)&lgArrByte, sizeof(lgArrByte));
+  write(os, lgArrByte);
 
   const uint8_t flagsByte(this->makeFlagsByte(compact));
-  os.write((char*)&flagsByte, sizeof(flagsByte));
+  write(os, flagsByte);
   const uint8_t curMinByte((uint8_t) curMin);
-  os.write((char*)&curMinByte, sizeof(curMinByte));
+  write(os, curMinByte);
   const uint8_t modeByte(this->makeModeByte());
-  os.write((char*)&modeByte, sizeof(modeByte));
+  write(os, modeByte);
 
   // estimator data
-  os.write((char*)&hipAccum, sizeof(hipAccum));
-  os.write((char*)&kxq0, sizeof(kxq0));
-  os.write((char*)&kxq1, sizeof(kxq1));
+  write(os, hipAccum);
+  write(os, kxq0);
+  write(os, kxq1);
 
   // array data
-  os.write((char*)&numAtCurMin, sizeof(numAtCurMin));
+  write(os, numAtCurMin);
 
   const int auxCount = (auxHashMap == nullptr ? 0 : auxHashMap->getAuxCount());
-  os.write((char*)&auxCount, sizeof(auxCount));
-  os.write((char*)hllByteArr.data(), getHllByteArrBytes());
+  write(os, auxCount);
+  write(os, hllByteArr.data(), getHllByteArrBytes());
 
   // aux map if HLL_4
   if (this->tgtHllType == HLL_4) {
     if (auxHashMap != nullptr) {
       if (compact) {
         for (uint32_t coupon: *auxHashMap) {
-          os.write((char*)&coupon, sizeof(coupon));
+          write(os, coupon);
         }
       } else {
-        os.write((char*)auxHashMap->getAuxIntArr(), auxHashMap->getUpdatableSizeBytes());
+        write(os, auxHashMap->getAuxIntArr(), auxHashMap->getUpdatableSizeBytes());
       }
     } else if (!compact) {
       // if updatable, we write even if currently unused so the binary can be wrapped