Class: BaseX

Inherits:
Object
  • Object
show all
Defined in:
lib/base_x.rb,
lib/base_x/version.rb

Defined Under Namespace

Classes: EmptyString, InvalidNumeral

Constant Summary collapse

EXAMPLE_TOKEN =

Random 64-bit token

"\xFC\x8E\x3C\x91\x7D\x58\x36\x8B"
Binary =

Binary

BaseX.new("01")
Base16L =

Base 16

BaseX.new((digits + %w[a b c d e f]).join)
Base16U =
BaseX.new((digits + %w[A B C D E F]).join)
Base16 =
BaseX::Base16L
Hex =
BaseX::Base16
Hexadecimal =
BaseX::Base16
Base30L =

Base 30, in case the number could change case no “u” following Crockford’s probabilistic fear of accidental obscenity (I caluclate the probability of obscenity, with “u”, is about 1 in 2^13 for any given random 3-letter string (about 1 in 8000); when encoding 128bit tokens, after generating about 225 random tokens you have a 50% chance of having produced an “obscene token”.)

BaseX.new((digits + lowercase - %w[0 1 i l o u]).join)
Base30U =
BaseX.new((digits + uppercase - %w[0 1 I L O U]).join)
Base31L =

Base 31, in case the number could change case

BaseX.new((digits + lowercase - %w[0 1 i l o]).join)
Base31U =
BaseX.new((digits + uppercase - %w[0 1 I L O]).join)
RFC4648Base32 =

Base 32 schemes

BaseX.new((uppercase + digits - %w[0 1 8 9]).join)
CrockfordBase32 =
BaseX.new((digits + uppercase - %w[I L O U]).join)
BitcoinBase58 =

Base58 schemes

BaseX.new((digits + uppercase + lowercase - %w[0 O I l]).join)
FlickrBase58 =
BaseX.new((digits + lowercase + uppercase - %w[0 O I l]).join)
GMPBase58 =
BaseX.new((digits + uppercase + lowercase - %w[w x y z]).join)
Base58 =
BaseX::BitcoinBase58
NewBase60 =

NewBase60, has an underscore as per tantek.pbworks.com/w/page/19402946/NewBase60

BaseX.new((digits + uppercase + %w[_] + lowercase - %w[O I l]).join)
Base62DUL =

Base62; digits, upper, lower

BaseX.new((digits + uppercase + lowercase).join)
Base62DLU =
BaseX.new((digits + lowercase + uppercase).join)
Base62LDU =
BaseX.new((lowercase + digits + uppercase).join)
Base62LUD =
BaseX.new((lowercase + uppercase + digits).join)
Base62UDL =
BaseX.new((uppercase + digits + lowercase).join)
Base62ULD =
BaseX.new((uppercase + lowercase + digits).join)
Base62 =
BaseX::Base62DUL
URLBase64 =

URL Base 64

BaseX.new((uppercase + lowercase + digits + %w[- _]).join)
Z85 =

ZeroMQ Base 85 Process your data back and forth between 4-byte and 5-byte chunks and you’ll be compatible with the Z85 standard

BaseX.new((digits + lowercase + uppercase + %w|. - : + = ^ ! / * ? & < > ( ) [ ] { } @ % $ #|).join)
Base256 =

Binary string encoding; turn binary strings into Bignums and back

BaseX.new((0..255).map(&:chr).join)
VERSION =
"0.8.0"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(numerals) ⇒ BaseX

Returns a new instance of BaseX.



80
81
82
83
84
85
86
87
# File 'lib/base_x.rb', line 80

def initialize(numerals)
  numerals.chars.size > 1 or
    raise ArgumentError.new("Need at least two numerals to express numbers! Numeral given: #{numerals.inspect}") 
  numerals.chars.size == numerals.chars.uniq.size or
    raise ArgumentError.new("Duplicate characters found in numerals definition: #{numerals.inspect}") 
  @numerals = numerals
  @base     = numerals.size
end

Instance Attribute Details

#baseObject (readonly)

Returns the value of attribute base.



78
79
80
# File 'lib/base_x.rb', line 78

def base
  @base
end

#numeralsObject (readonly)

Returns the value of attribute numerals.



77
78
79
# File 'lib/base_x.rb', line 77

def numerals
  @numerals
end

Class Method Details

.base(n) ⇒ Object



42
43
44
45
46
# File 'lib/base_x.rb', line 42

def self.base(n)
  n.between?(2, 62) or raise ArgumentError.new("Base #{n} is not valid; base must be at least 2 and at most 62")
  digits_uppercase_lowercase = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  new(digits_uppercase_lowercase[0...n])
end

.basesObject

Outputs an array of [base name, base size, example, and numerals] for all built-in bases



49
50
51
52
53
54
55
56
57
# File 'lib/base_x.rb', line 49

def self.bases
  constants
    .map     { |const_name| [const_name, const_get(const_name)] }
    .select  { |const_name, base| base.is_a?(BaseX) }
    .sort_by { |const_name, base| [base.base, const_name.to_s] }
    .map do |const_name, base|
      [const_name.to_s, base.base, base.encode(EXAMPLE_TOKEN), base.numerals]
    end
end

.bases_tableObject



59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/base_x.rb', line 59

def self.bases_table
  bases.map do |name, base_size, example, numerals|
    example = example[0...21] + "" if example.size > 22
    [
      name,
      base_size,
      example.inspect.include?("\\")  ? example.inspect  : example,
      numerals.inspect.include?("\\") ? numerals.inspect : numerals,
    ]
  end.map do |array|
    "%-15s %-3s %-22s %s" % array
  end.join("\n")
end

.decode(encoded, opts) ⇒ Object



36
37
38
39
40
# File 'lib/base_x.rb', line 36

def self.decode(encoded, opts)
  opts[:numerals].respond_to?(:index) or
    raise ArgumentError.new("A string of numerals must be provided, e.g. BaseX.encode(\"bcaffag\", numerals: \"abcdefg\")")
  new(opts[:numerals]).decode(encoded)
end

.encode(string, opts) ⇒ Object



30
31
32
33
34
# File 'lib/base_x.rb', line 30

def self.encode(string, opts)
  opts[:numerals].respond_to?(:index) or
    raise ArgumentError.new("A string of numerals must be provided, e.g. BaseX.encode(\"Hello World\", numerals: \"abcdefg\")")
  new(opts[:numerals]).encode(string)
end

.integer_to_string(int, opts) ⇒ Object



24
25
26
27
28
# File 'lib/base_x.rb', line 24

def self.integer_to_string(int, opts)
  opts[:numerals].respond_to?(:index) or
    raise ArgumentError.new("A string of numerals must be provided, e.g. BaseX.integer_to_string(123, numerals: \"abcdefg\")")
  new(opts[:numerals]).integer_to_string(int)
end


73
74
75
# File 'lib/base_x.rb', line 73

def self.print_bases
  puts bases_table
end

.string_to_integer(string, opts) ⇒ Object



18
19
20
21
22
# File 'lib/base_x.rb', line 18

def self.string_to_integer(string, opts)
  opts[:numerals].respond_to?(:index) or
    raise ArgumentError.new("A string of numerals must be provided, e.g. BaseX.string_to_integer(\"bcde\", numerals: \"abcdefg\")")
  new(opts[:numerals]).string_to_integer(string)
end

Instance Method Details

#decode(encoded) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/base_x.rb', line 126

def decode(encoded)
  return "" if encoded.size == 0
  int = string_to_integer(encoded)
  decoded = Base256.integer_to_string(int)
  decoded_number_size = 256**(decoded.size)
  encoded_number_size = base**(encoded.size)

  # encoded_number_size / base < decoded_number_size <= encoded_number_size
  while decoded_number_size <= encoded_number_size / base
    decoded = "\x00" + decoded
    decoded_number_size *= 256
  end

  decoded
end

#encode(string) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/base_x.rb', line 111

def encode(string)
  return "" if string.size == 0
  int = string.each_byte.reduce(0) { |int, byte| int *= 256; int + byte }
  encoded = integer_to_string(int)
  string_number_size = 256**(string.size)
  encoded_number_size = base**(encoded.size)

  while encoded_number_size < string_number_size
    encoded = @numerals[0] + encoded
    encoded_number_size *= base
  end

  encoded
end

#integer_to_string(int) ⇒ Object



100
101
102
103
104
105
106
107
108
109
# File 'lib/base_x.rb', line 100

def integer_to_string(int)
  return @numerals[0] if int == 0
  string = ""
  while int > 0
    char = @numerals[int % base]
    string << char
    int /= base
  end
  string.reverse
end

#string_to_integer(string) ⇒ Object

Raises:



89
90
91
92
93
94
95
96
97
98
# File 'lib/base_x.rb', line 89

def string_to_integer(string)
  raise EmptyString.new unless string.size > 0
  integer = 0
  string.each_char do |char|
    integer *= base
    numeral_value = @numerals.index(char) or raise InvalidNumeral.new(char)
    integer += numeral_value
  end
  integer
end