///|
fn split_srcset_candidate(candidate : StringView) -> (StringView, StringView) {
  let mut pos = 0
  while pos < candidate.length() {
    let ch = candidate.get_char(pos).unwrap()
    if is_html_whitespace_char(ch) {
      let url = candidate[0:pos]
      let descriptor = candidate[pos:candidate.length()].trim()
      return (url, descriptor)
    } else {
      pos += ch.utf16_len()
    }
  }
  (candidate, "")
}

///|
fn push_sanitized_srcset_candidate(
  out : Array[String],
  url_policy : UrlPolicy,
  rule : UrlRule,
  tag_name : String,
  attr_name : String,
  candidate : StringView,
) -> Bool {
  let candidate = candidate.trim()
  if candidate.is_empty() {
    return true
  }
  let (url_token, descriptor) = split_srcset_candidate(candidate)
  match
    sanitize_url_attribute_value_with_rule(
      url_policy,
      rule,
      tag_name,
      attr_name,
      url_token.to_owned(),
      apply_filter=false,
    ) {
    Some(cleaned_url) => {
      if descriptor.is_empty() {
        out.push(cleaned_url)
      } else {
        out.push(cleaned_url + " " + descriptor.to_owned())
      }
      true
    }
    None => false
  }
}

///|
fn sanitize_srcset_attribute_value(
  policy : SanitizationPolicy,
  tag_name : String,
  attr_name : String,
  value : String,
) -> String? {
  guard policy.url_policy.lookup_rule(tag_name, attr_name) is Some(rule) else {
    return None
  }
  let value = match
    policy.url_policy.apply_url_filter(tag_name, attr_name, value) {
    Some(filtered) => filtered
    None => return None
  }
  let view = value[:].trim()
  if view.is_empty() {
    return None
  }
  let out : Array[String] = []
  let mut start = 0
  let mut pos = 0
  while pos < view.length() {
    let ch = view.get_char(pos).unwrap()
    if ch == ',' {
      let candidate = view[start:pos]
      if !push_sanitized_srcset_candidate(
          out,
          policy.url_policy,
          rule,
          tag_name,
          attr_name,
          candidate,
        ) {
        return None
      }
      pos += 1
      start = pos
    } else {
      pos += ch.utf16_len()
    }
  }
  let candidate = view[start:view.length()]
  if !push_sanitized_srcset_candidate(
      out,
      policy.url_policy,
      rule,
      tag_name,
      attr_name,
      candidate,
    ) {
    return None
  }
  if out.is_empty() {
    None
  } else {
    Some(out.join(", "))
  }
}

///|
fn push_sanitized_url_list_token(
  out : Array[String],
  url_policy : UrlPolicy,
  rule : UrlRule,
  tag_name : String,
  attr_name : String,
  token : StringView,
) -> Bool {
  let token = token.trim()
  if token.is_empty() {
    return true
  }
  match
    sanitize_url_attribute_value_with_rule(
      url_policy,
      rule,
      tag_name,
      attr_name,
      token.to_owned(),
      apply_filter=false,
    ) {
    Some(cleaned) => {
      out.push(cleaned)
      true
    }
    None => false
  }
}

///|
fn sanitize_space_separated_url_list_attribute_value(
  policy : SanitizationPolicy,
  tag_name : String,
  attr_name : String,
  value : String,
) -> String? {
  guard policy.url_policy.lookup_rule(tag_name, attr_name) is Some(rule) else {
    return None
  }
  let value = match
    policy.url_policy.apply_url_filter(tag_name, attr_name, value) {
    Some(filtered) => filtered
    None => return None
  }
  let view = value[:].trim()
  if view.is_empty() {
    return None
  }
  let out : Array[String] = []
  let mut start = 0
  let mut pos = 0
  while pos < view.length() {
    let ch = view.get_char(pos).unwrap()
    if is_html_whitespace_char(ch) {
      let token = view[start:pos]
      if !push_sanitized_url_list_token(
          out,
          policy.url_policy,
          rule,
          tag_name,
          attr_name,
          token,
        ) {
        return None
      }
      pos += ch.utf16_len()
      start = pos
    } else {
      pos += ch.utf16_len()
    }
  }
  let token = view[start:view.length()]
  if !push_sanitized_url_list_token(
      out,
      policy.url_policy,
      rule,
      tag_name,
      attr_name,
      token,
    ) {
    return None
  }
  if out.is_empty() {
    None
  } else {
    Some(out.join(" "))
  }
}