Constant time compare

primitive ConstantTimeCompare
  """
  Return true if the two `ByteSeq`s have equal contents. The time taken is
  independent of the contents, so comparing a secret against a value an
  attacker supplies does not tell them where the two first differ.

  ```pony
  if ConstantTimeCompare(expected_mac, supplied_mac) then
    // ...
  end
  ```

  Sequences of different sizes are not equal, and that is answered before any
  byte is read.
  """
  fun apply[S: ByteSeq box = ByteSeq box](xs: S, ys: S): Bool =>
    _compare(xs, ys)

  fun _compare(xs: ByteSeq box, ys: ByteSeq box): Bool =>
    // Non-generic so it can call `_Unreachable`. That primitive takes a
    // `SourceLoc` default of `__loc`, and `__loc` inside the generic `apply`
    // does not compile.
    if xs.size() != ys.size() then
      false
    else
      var v = U8(0)
      var i: USize = 0
      while i < xs.size() do
        // `i < xs.size()` bounds `xs(i)?`, and the size check above bounds
        // `ys(i)?`, so neither read can raise; the `else` only satisfies the
        // compiler.
        try
          v = v or (xs(i)? xor ys(i)?)
        else
          _Unreachable()
        end
        i = i + 1
      end
      v == 0
    end