///|
let base64_chars : String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

///|
fn base64_value(ch : Char) -> Int? {
  for i in 0.. Char {
  base64_chars.unsafe_get(value).to_int().unsafe_to_char()
}

///|
/// Encode one signed integer using Source Map Base64 VLQ.
pub fn encode_vlq(value : Int) -> String {
  let mut vlq = if value < 0 { ((0 - value) << 1) | 1 } else { value << 1 }
  let mut out = ""
  for ;; {
    let mut digit = vlq & 31
    vlq = vlq >> 5
    if vlq != 0 {
      digit = digit | 32
    }
    out = out + base64_char(digit).to_string()
    if vlq == 0 {
      break
    }
  }
  out
}

///|
/// Decode all signed values in one mapping segment.
pub fn decode_vlq_values(encoded : String) -> Array[Int] raise SourceMapError {
  let values : Array[Int] = Array::new()
  let mut offset = 0
  while offset < encoded.length() {
    let mut result = 0
    let mut shift = 0
    let start = offset
    for ;; {
      if offset >= encoded.length() {
        raise SourceMapError::At(start, "unterminated VLQ value")
      }
      let ch = encoded.unsafe_get(offset).to_int().unsafe_to_char()
      let digit = match base64_value(ch) {
        Some(value) => value
        None =>
          raise SourceMapError::At(
            offset,
            "invalid Base64 character '\{ch.to_string()}'",
          )
      }
      offset += 1
      result = result | ((digit & 31) << shift)
      if (digit & 32) == 0 {
        break
      }
      shift += 5
      if shift > 30 {
        raise SourceMapError::At(start, "VLQ value exceeds Int range")
      }
    }
    let negative = (result & 1) == 1
    let magnitude = result >> 1
    values.push(if negative { 0 - magnitude } else { magnitude })
  }
  values
}

///|
/// Decode exactly one signed Base64 VLQ integer.
pub fn decode_vlq(encoded : String) -> Int raise SourceMapError {
  let values = decode_vlq_values(encoded)
  if values.length() != 1 {
    raise SourceMapError::At(0, "expected exactly one VLQ value")
  }
  values[0]
}