defmodule SimdXml.Quick do @moduledoc """ Grep-mode fast path for simple `//tagname` extraction. Skips structural indexing entirely -- scans raw bytes with SIMD-accelerated `memchr` at near-memory-bandwidth speed. Use this when you need a single tag from many documents and do not need XPath predicates, attributes, or complex navigation. ## When to use Quick vs full parsing * **Use Quick** for simple tag extraction (`//tagname`) across many documents where speed is paramount. Quick scanners cannot evaluate predicates, navigate axes, or handle namespaces. * **Use `SimdXml.Batch`** when you need XPath predicates or more complex expressions but still want batch processing. * **Use `SimdXml.parse/1`** when you need full XPath or element navigation. ## Scanner lifecycle Create a scanner once with `new/1` and reuse it across documents. The scanner compiles the tag name into an optimized byte pattern -- this is the only allocation. All extraction functions are zero-allocation on the Elixir side. ## Limitations `extract_first/2` returns `nil` for elements that contain nested child elements. If you encounter this, fall back to the full parser. The scanner only handles simple text content between open and close tags. ## Examples iex> scanner = SimdXml.Quick.new("title") iex> SimdXml.Quick.extract_first(scanner, "Hello") "Hello" iex> scanner = SimdXml.Quick.new("title") iex> SimdXml.Quick.exists?(scanner, "Hello") true iex> scanner = SimdXml.Quick.new("missing") iex> SimdXml.Quick.exists?(scanner, "Hello") false iex> scanner = SimdXml.Quick.new("item") iex> SimdXml.Quick.count(scanner, "") 3 ## Related modules * `SimdXml.Batch` - batch processing with full XPath support * `SimdXml` - full parsing and XPath evaluation """ @enforce_keys [:ref, :tag] defstruct [:ref, :tag] @typedoc """ A compiled quick scanner for a single tag name. The `:ref` field holds the NIF resource reference. The `:tag` field stores the tag name for inspection. """ @type t :: %__MODULE__{ref: reference(), tag: String.t()} @doc """ Creates a scanner for the given tag name. This is the only allocation. The scanner compiles the tag name into a SIMD-optimized byte pattern that can be reused across any number of documents. ## Examples iex> scanner = SimdXml.Quick.new("claim") iex> scanner.tag "claim" """ @spec new(String.t()) :: t() def new(tag) when is_binary(tag) do ref = SimdXml.Native.quick_scanner_new(tag) %__MODULE__{ref: ref, tag: tag} end @doc """ Extracts the text content of the first matching tag. Returns `nil` if the tag is not found, or if the matched element contains nested child elements (in which case, use the full parser instead). ## Examples iex> scanner = SimdXml.Quick.new("title") iex> SimdXml.Quick.extract_first(scanner, "Hello") "Hello" iex> scanner = SimdXml.Quick.new("missing") iex> SimdXml.Quick.extract_first(scanner, "Hello") nil """ @spec extract_first(t(), binary()) :: String.t() | nil def extract_first(%__MODULE__{ref: ref}, data) when is_binary(data) do SimdXml.Native.quick_extract_first(ref, data) end @doc """ Checks whether the tag exists anywhere in the document. Faster than `extract_first/2` when you only need a boolean answer, because it short-circuits after the first match without extracting text. ## Examples iex> scanner = SimdXml.Quick.new("title") iex> SimdXml.Quick.exists?(scanner, "A") true iex> scanner = SimdXml.Quick.new("missing") iex> SimdXml.Quick.exists?(scanner, "A") false """ @spec exists?(t(), binary()) :: boolean() def exists?(%__MODULE__{ref: ref}, data) when is_binary(data) do SimdXml.Native.quick_exists(ref, data) end @doc """ Counts occurrences of the tag (both open tags and self-closing tags). ## Examples iex> scanner = SimdXml.Quick.new("item") iex> SimdXml.Quick.count(scanner, "A") 2 iex> scanner = SimdXml.Quick.new("missing") iex> SimdXml.Quick.count(scanner, "") 0 """ @spec count(t(), binary()) :: non_neg_integer() def count(%__MODULE__{ref: ref}, data) when is_binary(data) do SimdXml.Native.quick_count(ref, data) end end defimpl Inspect, for: SimdXml.Quick do def inspect(%SimdXml.Quick{tag: tag}, _opts) do "#SimdXml.Quick<#{inspect(tag)}>" end end