Class: EncodedId::Encoders::HashidOrdinalAlphabetSeparatorGuards

Inherits:
Object
  • Object
show all
Includes:
HashidConsistentShuffle
Defined in:
lib/encoded_id/encoders/hashid_ordinal_alphabet_separator_guards.rb

Overview

Prepares and partitions the character sets for HashID encoding.

This class is responsible for splitting a single input alphabet into three disjoint sets:

  1. Alphabet: Main characters used to encode numbers

  2. **Separators (seps)**: Characters that separate encoded numbers in the hash

  3. Guards: Characters added at boundaries to meet minimum length requirements

Initialization Process:

Step 1: Start with default separators (“cfhistuCFHISTU”) Step 2: Ensure separators and alphabet are disjoint (remove overlaps) Step 3: Shuffle separators using the salt Step 4: Balance alphabet-to-separator ratio (target ≈ 3.5:1) Step 5: Create guards from alphabet or separators (target ≈ 12:1 alphabet-to-guards) Step 6: Shuffle alphabet using the salt

Character Set Ratios:

The algorithm maintains specific ratios between the character sets:

  • Alphabet : Separators ≈ 3.5 : 1 (SEP_DIV)

  • Alphabet : Guards ≈ 12 : 1 (GUARD_DIV)

These ratios ensure:

  • Enough separators to avoid patterns in multi-number hashes

  • Guards are rare enough to not waste space but common enough to be useful

  • Alphabet is large enough for efficient encoding (shorter hashes)

Why Ordinals?

All characters are stored as integer ordinals (Unicode codepoints) rather than strings. This provides:

  • Faster comparisons and lookups

  • More efficient memory usage

  • Direct array indexing without string allocations

Constant Summary collapse

SEP_DIV =

Target ratio of alphabet to separators (alphabet.length / seps.length ≈ 3.5)

3.5
DEFAULT_SEPS =

Default separator characters - chosen to be visually distinct and common in many fonts

"cfhistuCFHISTU".chars.map(&:ord).freeze
GUARD_DIV =

Target ratio of alphabet to guards (alphabet.length / guards.length ≈ 12)

12.0
SPACE_CHAR =

Space character ordinal - used as a placeholder when removing characters

" ".ord

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from HashidConsistentShuffle

#consistent_shuffle!

Constructor Details

#initialize(alphabet, salt) ⇒ HashidOrdinalAlphabetSeparatorGuards

Initialize and partition the character sets.

Takes an alphabet and salt, then:

  1. Converts all characters to ordinals (integer codepoints)

  2. Partitions the alphabet into separators, guards, and the remaining alphabet

  3. Shuffles each set deterministically using the salt

  4. Balances the ratios between the sets

  5. Creates escaped versions for use with String#tr

All arrays are frozen after setup to prevent accidental modification.

Parameters:

  • alphabet (Alphabet)

    The character set to partition

  • salt (String)

    The salt used for shuffling



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/encoded_id/encoders/hashid_ordinal_alphabet_separator_guards.rb', line 79

def initialize(alphabet, salt)
  @alphabet = alphabet.characters.chars.map(&:ord)
  @salt = salt.chars.map(&:ord)

  setup_seps
  setup_guards

  # Pre-compute escaped versions for String#tr operations during decode.
  # This escapes special characters like '-', '\\', and '^' that have
  # special meaning in tr() character ranges.
  @seps_tr_selector = escape_characters_string_for_tr(@seps.map(&:chr))
  @guards_tr_selector = escape_characters_string_for_tr(@guards.map(&:chr))

  @alphabet.freeze
  @seps.freeze
  @guards.freeze
end

Instance Attribute Details

#alphabetObject (readonly)

: Array



98
99
100
# File 'lib/encoded_id/encoders/hashid_ordinal_alphabet_separator_guards.rb', line 98

def alphabet
  @alphabet
end

#guardsObject (readonly)

: Array



100
101
102
# File 'lib/encoded_id/encoders/hashid_ordinal_alphabet_separator_guards.rb', line 100

def guards
  @guards
end

#guards_tr_selectorObject (readonly)

: String



102
103
104
# File 'lib/encoded_id/encoders/hashid_ordinal_alphabet_separator_guards.rb', line 102

def guards_tr_selector
  @guards_tr_selector
end

#saltObject (readonly)

: Array



97
98
99
# File 'lib/encoded_id/encoders/hashid_ordinal_alphabet_separator_guards.rb', line 97

def salt
  @salt
end

#sepsObject (readonly)

: Array



99
100
101
# File 'lib/encoded_id/encoders/hashid_ordinal_alphabet_separator_guards.rb', line 99

def seps
  @seps
end

#seps_tr_selectorObject (readonly)

: String



101
102
103
# File 'lib/encoded_id/encoders/hashid_ordinal_alphabet_separator_guards.rb', line 101

def seps_tr_selector
  @seps_tr_selector
end