# `JsonRemedy.Utils.CharUtils`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L1)

Optimized UTF-8 safe character navigation utilities for JSON parsing.

This module provides high-performance character-by-character navigation functions that handle
Unicode characters correctly while maintaining excellent performance for JSON string processing.
All functions use String.length/1 and String.at/2 for UTF-8 safety, avoiding byte-based
operations that could break on multi-byte characters.

## Performance Optimizations

- Early bounds checking to avoid unnecessary computations
- Optimized whitespace detection using compile-time constants
- Tail-recursive functions for memory efficiency
- Minimal pattern matching overhead
- Reduced function call overhead in hot paths

## Features

- UTF-8 safe character positioning using grapheme-based counting
- Comprehensive bounds checking with graceful error handling
- Defensive programming patterns against nil and invalid inputs
- Context-aware whitespace handling for JSON parsing scenarios
- Performance-optimized for common character navigation tasks

## Safety Guarantees

All functions in this module:
- Handle nil inputs gracefully without raising exceptions
- Use String.length/1 for character counting (not byte_size/1)
- Use String.at/2 for character access (not binary pattern matching)
- Return consistent result types for error conditions
- Preserve UTF-8 character boundaries correctly

## Examples

    iex> JsonRemedy.Utils.CharUtils.get_char_at("café", 3, nil)
    "é"

    iex> JsonRemedy.Utils.CharUtils.skip_to_character("hello world", "w", 0)
    6

    iex> JsonRemedy.Utils.CharUtils.whitespace?(" ")
    true

    iex> JsonRemedy.Utils.CharUtils.char_at_position_safe("test", 10)
    nil

# `char_result`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L54)

```elixir
@type char_result() :: String.t() | nil
```

# `position`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L53)

```elixir
@type position() :: non_neg_integer()
```

# `search_result`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L55)

```elixir
@type search_result() :: non_neg_integer() | nil
```

# `char_at_position_safe`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L293)

```elixir
@spec char_at_position_safe(String.t() | nil, position()) :: char_result()
```

Safe wrapper for String.at/2 that handles nil inputs gracefully.

Optimized thin wrapper that leverages String.at/2's built-in bounds checking.
Provides a safe way to access characters at specific positions without raising
exceptions. Returns nil for any error condition.

## Parameters

- `input` - The input string (may be nil)
- `position` - Zero-based character position (non-negative integer)

## Returns

- The character at the position if valid
- `nil` if position is out of bounds, input is nil, or invalid parameters

## Examples

    iex> char_at_position_safe("hello", 1)
    "e"

    iex> char_at_position_safe("café", 3)
    "é"

    iex> char_at_position_safe("test", 10)
    nil

    iex> char_at_position_safe("", 0)
    nil

    iex> char_at_position_safe(nil, 0)
    nil

# `get_char_at`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L93)

```elixir
@spec get_char_at(String.t() | nil, position(), any()) :: any()
```

Safely retrieves character at specified position with default fallback.

Optimized for performance with early bounds checking and minimal overhead.
Returns the character at the given position, or the default value if the position
is out of bounds, the input is nil, or any other error condition occurs.

## Parameters

- `input` - The input string (may be nil)
- `position` - Zero-based character position (non-negative integer)
- `default` - Value to return if character cannot be retrieved

## Returns

- The character at the position if valid
- The default value if position is out of bounds or input is nil/invalid

## Examples

    iex> get_char_at("hello", 0, nil)
    "h"

    iex> get_char_at("café", 3, nil)
    "é"

    iex> get_char_at("test", 10, "?")
    "?"

    iex> get_char_at(nil, 0, "default")
    "default"

    iex> get_char_at("", 0, nil)
    nil

# `skip_to_character`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L138)

```elixir
@spec skip_to_character(String.t() | nil, String.t() | nil, position()) ::
  search_result()
```

Searches for target character starting from given position.

Optimized with early bounds checking and efficient search loop.
Finds the first occurrence of the target character in the input string, starting
the search from the specified position. Returns the position where the character
was found, or nil if not found or invalid input.

## Parameters

- `input` - The input string to search (may be nil)
- `target_char` - The character to search for (may be nil)
- `start_pos` - Starting position for the search (non-negative integer)

## Returns

- Position (integer) where the character was found
- `nil` if character not found, input is nil, or invalid parameters

## Examples

    iex> skip_to_character("hello world", "w", 0)
    6

    iex> skip_to_character("test", "s", 2)
    2

    iex> skip_to_character("café résumé", "é", 0)
    3

    iex> skip_to_character("hello", "x", 0)
    nil

    iex> skip_to_character(nil, "a", 0)
    nil

# `skip_whitespaces_at`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L190)

```elixir
@spec skip_whitespaces_at(String.t() | nil, position(), position()) :: position()
```

Skips whitespace characters from start position up to end position.

Highly optimized with compile-time whitespace constants and efficient loop.
Advances through the input string starting from start_pos, skipping over whitespace
characters until a non-whitespace character is found or end_pos is reached.

## Parameters

- `input` - The input string (may be nil)
- `start_pos` - Starting position (non-negative integer)
- `end_pos` - Maximum position to check (non-negative integer)

## Returns

- Position (integer) of first non-whitespace character or end_pos if all whitespace
- start_pos if input is nil or invalid parameters

## Examples

    iex> skip_whitespaces_at("   hello", 0, 8)
    3

    iex> skip_whitespaces_at("hello", 0, 5)
    0

    iex> skip_whitespaces_at("  \t\n  test", 0, 12)
    6

    iex> skip_whitespaces_at("   ", 0, 3)
    3

    iex> skip_whitespaces_at(nil, 0, 5)
    0

# `whitespace?`
[🔗](https://github.com/nshkrdotcom/json_remedy/blob/v0.2.1/lib/json_remedy/utils/char_utils.ex#L251)

```elixir
@spec whitespace?(String.t() | nil) :: boolean()
```

Checks if a character is considered whitespace for JSON parsing.

Optimized with compile-time constant for maximum performance.
Determines whether the given character should be treated as whitespace in the
context of JSON parsing. Handles both ASCII and Unicode whitespace characters.

## Parameters

- `char` - The character to check (may be nil or non-string)

## Returns

- `true` if the character is whitespace
- `false` if the character is not whitespace or is nil/invalid

## Examples

    iex> whitespace?(" ")
    true

    iex> whitespace?("\t")
    true

    iex> whitespace?("\n")
    true

    iex> whitespace?("\r")
    true

    iex> whitespace?("a")
    false

    iex> whitespace?(nil)
    false

    iex> whitespace?("")
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
