// Copyright (C) 2019 Donovan Baarda 
// MoonBit scalar port: Copyright (C) 2026 MoonLibSync contributors
// SPDX-License-Identifier: LGPL-2.1-or-later
// Ported from librsync e364852674780e43d578e4239128ff7014190ed3
// src/rabinkarp.h; scalar update only, no unrolling or power tables.

///|
#warnings("-unused_field")
priv struct RabinKarp {
  mut count : Int32
  mut hash : UInt32
  mut mult : UInt32
}

///|
/// Internal operations currently consumed by whitebox tests only.
#warnings("-unused_value")
fn RabinKarp::new() -> RabinKarp {
  { count: 0, hash: 1U, mult: 1U, }
}

///|
/// Capacity is checked independently from modular checksum arithmetic.
fn RabinKarp::check_capacity(
  self : RabinKarp,
  incoming : Int32,
) -> Unit raise SyncError {
  guard self.count >= 0 && incoming >= 0 && incoming <= 2147483647 - self.count else {
    raise IntegerOutOfRange
  }
}

///|
#warnings("-unused_value")
fn RabinKarp::rollin(self : RabinKarp, incoming : Byte) -> Unit raise SyncError {
  self.check_capacity(1)
  self.hash = self.hash * 0x08104225U + incoming.to_uint()
  self.count = self.count + 1
  self.mult = self.mult * 0x08104225U
}

///|
/// Preflight the entire batch before modifying any state.
#warnings("-unused_value")
fn RabinKarp::update(
  self : RabinKarp,
  input : BytesView,
) -> Unit raise SyncError {
  self.check_capacity(input.length())
  for byte in input {
    self.hash = self.hash * 0x08104225U + byte.to_uint()
    self.mult = self.mult * 0x08104225U
  }
  self.count = self.count + input.length()
}

///|
/// Caller must supply the actual outgoing first byte; no window is stored.
#warnings("-unused_value")
fn RabinKarp::rotate(
  self : RabinKarp,
  outgoing : Byte,
  incoming : Byte,
) -> Unit raise SyncError {
  guard self.count > 0 else { raise InvalidOptions("empty_rabinkarp_window") }
  self.hash = self.hash * 0x08104225U +
    incoming.to_uint() -
    self.mult * (outgoing.to_uint() + 0x08104224U)
}

///|
/// EOF shortening uses the newly reduced multiplier.
#warnings("-unused_value")
fn RabinKarp::rollout(
  self : RabinKarp,
  outgoing : Byte,
) -> Unit raise SyncError {
  guard self.count > 0 else { raise InvalidOptions("empty_rabinkarp_window") }
  self.count = self.count - 1
  self.mult = self.mult * 0x98f009adU
  self.hash = self.hash - self.mult * (outgoing.to_uint() + 0x08104224U)
}

///|
#warnings("-unused_value")
fn RabinKarp::digest(self : RabinKarp) -> UInt32 {
  self.hash
}