docs: @SuppressWarnings("null") // bytes will be null only if empty == true
    public byte[] toByteArray(final ArrayOfItemsSerDe<? super T> serDe, final Class<?> clazz) {
      final int preLongs, numMarkBytes, outBytes;
      final boolean empty = (r_ == 0) && (h_ == 0);
      byte[] itemBytes = null; // for serialized items from serDe
      int flags = marks_ == null ? 0 : GADGET_FLAG_MASK;
  
      if (empty) {
        preLongs = Family.VAROPT.getMinPreLongs();
        outBytes = Family.VAROPT.getMinPreLongs() << 3; // only contains the minimum header info
        flags |= EMPTY_FLAG_MASK;
      } else {
        preLongs = (r_ == 0 ? PreambleUtil.VO_WARMUP_PRELONGS : Family.VAROPT.getMaxPreLongs());
        itemBytes = serDe.serializeToByteArray(getDataSamples(clazz));
        numMarkBytes = marks_ == null ? 0 : ArrayOfBooleansSerDe.computeBytesNeeded(h_);
        outBytes = (preLongs << 3) + (h_ * Double.BYTES) + numMarkBytes + itemBytes.length;
      }
      final byte[] outArr = new byte[outBytes];
      final WritableMemory mem = WritableMemory.wrap(outArr);
  
      // build first preLong
      PreambleUtil.insertPreLongs(mem, preLongs);               // Byte 0
      PreambleUtil.insertLgResizeFactor(mem, rf_.lg());
      PreambleUtil.insertSerVer(mem, SER_VER);                  // Byte 1
      PreambleUtil.insertFamilyID(mem, Family.VAROPT.getID());  // Byte 2
      PreambleUtil.insertFlags(mem, flags);                     // Byte 3
      PreambleUtil.insertK(mem, k_);                            // Bytes 4-7
  
      if (!empty) {
        PreambleUtil.insertN(mem, n_);                                      // Bytes 8-15
        PreambleUtil.insertHRegionItemCount(mem, h_);           // Bytes 16-19
        PreambleUtil.insertRRegionItemCount(mem, r_);           // Bytes 20-23
        if (r_ > 0) {
          PreambleUtil.insertTotalRWeight(mem, totalWtR_);      // Bytes 24-31
        }
  
        // write the first h_ weights
        int offset = preLongs << 3;
        for (int i = 0; i < h_; ++i) {
          mem.putDouble(offset, weights_.get(i));
          offset += Double.BYTES;
        }
  
        // write the first h_ marks, iff we have a gadget
        if (marks_ != null) {
          final byte[] markBytes;
          markBytes = MARK_SERDE.serializeToByteArray(marks_.subList(0, h_).toArray(new Boolean[0]));
          mem.putByteArray(offset, markBytes, 0, markBytes.length);
          offset += markBytes.length;
        }
  
        // write the sample items, using offset from earlier
        mem.putByteArray(offset, itemBytes, 0, itemBytes.length);
      }
  
      return outArr;
    }