docs: @SuppressWarnings("restriction")
    public static long[] hash(final Memory mem, final long offsetBytes, final long lengthBytes,
        final long seed, final long[] hashOut) {
      if ((mem == null) || (mem.getCapacity() == 0L)) {
        return emptyOrNull(seed, hashOut);
      }
      final Object uObj = ((WritableMemory) mem).getArray(); //may be null
      long cumOff = mem.getCumulativeOffset() + offsetBytes;
  
      long h1 = seed;
      long h2 = seed;
      long rem = lengthBytes;
  
      // Process the 128-bit blocks (the body) into the hash
      while (rem >= 16L) {
        final long k1 = unsafe.getLong(uObj, cumOff);     //0, 16, 32, ...
        final long k2 = unsafe.getLong(uObj, cumOff + 8); //8, 24, 40, ...
        cumOff += 16L;
        rem -= 16L;
  
        h1 ^= mixK1(k1);
        h1 = Long.rotateLeft(h1, 27);
        h1 += h2;
        h1 = (h1 * 5) + 0x52dce729L;
  
        h2 ^= mixK2(k2);
        h2 = Long.rotateLeft(h2, 31);
        h2 += h1;
        h2 = (h2 * 5) + 0x38495ab5L;
      }
  
      // Get the tail (if any): 1 to 15 bytes
      if (rem > 0L) {
        long k1 = 0;
        long k2 = 0;
        switch ((int) rem) {
          case 15: {
            k2 ^= (unsafe.getByte(uObj, cumOff + 14) & 0xFFL) << 48;
          }
          //$FALL-THROUGH$
          case 14: {
            k2 ^= (unsafe.getShort(uObj, cumOff + 12) & 0xFFFFL) << 32;
            k2 ^= (unsafe.getInt(uObj, cumOff + 8) & 0xFFFFFFFFL);
            k1 = unsafe.getLong(uObj, cumOff);
            break;
          }
  
          case 13: {
            k2 ^= (unsafe.getByte(uObj, cumOff + 12) & 0xFFL) << 32;
          }
          //$FALL-THROUGH$
          case 12: {
            k2 ^= (unsafe.getInt(uObj, cumOff + 8) & 0xFFFFFFFFL);
            k1 = unsafe.getLong(uObj, cumOff);
            break;
          }
  
          case 11: {
            k2 ^= (unsafe.getByte(uObj, cumOff + 10) & 0xFFL) << 16;
          }
          //$FALL-THROUGH$
          case 10: {
            k2 ^= (unsafe.getShort(uObj, cumOff +  8) & 0xFFFFL);
            k1 = unsafe.getLong(uObj, cumOff);
            break;
          }
  
          case  9: {
            k2 ^= (unsafe.getByte(uObj, cumOff +  8) & 0xFFL);
          }
          //$FALL-THROUGH$
          case  8: {
            k1 = unsafe.getLong(uObj, cumOff);
            break;
          }
  
          case  7: {
            k1 ^= (unsafe.getByte(uObj, cumOff +  6) & 0xFFL) << 48;
          }
          //$FALL-THROUGH$
          case  6: {
            k1 ^= (unsafe.getShort(uObj, cumOff +  4) & 0xFFFFL) << 32;
            k1 ^= (unsafe.getInt(uObj, cumOff) & 0xFFFFFFFFL);
            break;
          }
  
          case  5: {
            k1 ^= (unsafe.getByte(uObj, cumOff +  4) & 0xFFL) << 32;
          }
          //$FALL-THROUGH$
          case  4: {
            k1 ^= (unsafe.getInt(uObj, cumOff) & 0xFFFFFFFFL);
            break;
          }
  
          case  3: {
            k1 ^= (unsafe.getByte(uObj, cumOff +  2) & 0xFFL) << 16;
          }
          //$FALL-THROUGH$
          case  2: {
            k1 ^= (unsafe.getShort(uObj, cumOff) & 0xFFFFL);
            break;
          }
  
          case  1: {
            k1 ^= (unsafe.getByte(uObj, cumOff) & 0xFFL);
            break;
          }
          //default: break; //can't happen
        }
  
        h1 ^= mixK1(k1);
        h2 ^= mixK2(k2);
      }
      return finalMix128(h1, h2, lengthBytes, hashOut);
    }