Class: EncodeM::Encoder

Inherits:
Object
  • Object
show all
Defined in:
lib/encode_m/encoder.rb

Constant Summary collapse

KEY_DELIMITER =

Constants from the M language subscript encoding

0x00
STR_SUB_ESCAPE =

Terminator

0x01
SUBSCRIPT_ZERO =

Escape in strings

0x80
STR_SUB_PREFIX =

Zero value

0xFF
NEG_MNTSSA_END =

String marker

0xFF
NEG_EXPONENTS =

Negative exponent bytes (decreasing magnitude = increasing byte value)

{
  9 => 0x3B,  # -999,999,999 to -100,000,000
  8 => 0x3C,  # -99,999,999 to -10,000,000
  7 => 0x3D,  # -9,999,999 to -1,000,000
  6 => 0x3E,  # -999,999 to -100,000
  5 => 0x3F,  # -99,999 to -10,000
  4 => 0x40,  # -9,999 to -1,000
  3 => 0x41,  # -999 to -100
  2 => 0x42,  # -99 to -10
  1 => 0x43   # -9 to -1
}.freeze
POS_EXPONENTS =

Positive exponent bytes (increasing magnitude = increasing byte value)

{
  1 => 0xBC,  # 1 to 9
  2 => 0xBD,  # 10 to 99
  3 => 0xBE,  # 100 to 999
  4 => 0xBF,  # 1,000 to 9,999
  5 => 0xC0,  # 10,000 to 99,999
  6 => 0xC1,  # 100,000 to 999,999
  7 => 0xC2,  # 1,000,000 to 9,999,999
  8 => 0xC3,  # 10,000,000 to 99,999,999
  9 => 0xC4   # 100,000,000 to 999,999,999
}.freeze
POS_CODE =

Encoding tables for digit pairs (00-99)

[
  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
  0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
  0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
  0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
  0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
  0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
  0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
  0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
  0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a
].freeze
NEG_CODE =
[
  0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5,
  0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, 0xe7, 0xe6, 0xe5,
  0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, 0xd7, 0xd6, 0xd5,
  0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, 0xc7, 0xc6, 0xc5,
  0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, 0xb7, 0xb6, 0xb5,
  0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8, 0xa7, 0xa6, 0xa5,
  0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95,
  0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85,
  0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75,
  0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65
].freeze

Class Method Summary collapse

Class Method Details

.encode_decimal(value, result = []) ⇒ Object



108
109
110
111
# File 'lib/encode_m/encoder.rb', line 108

def self.encode_decimal(value, result = [])
  # For now, just convert to integer
  encode_integer(value.to_i)
end

.encode_integer(value) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/encode_m/encoder.rb', line 65

def self.encode_integer(value)
  return [SUBSCRIPT_ZERO].pack('C') if value == 0

  is_negative = value < 0
  abs_value = is_negative ? -value : value

  # Count the number of digits
  digit_count = abs_value.to_s.length

  # Get the appropriate exponent byte
  if is_negative
    exp_byte = NEG_EXPONENTS[digit_count] || NEG_EXPONENTS[9]
  else
    exp_byte = POS_EXPONENTS[digit_count] || POS_EXPONENTS[9]
  end

  result = [exp_byte]

  # Encode the mantissa as digit pairs
  cvt_table = is_negative ? NEG_CODE : POS_CODE

  # Convert number to pairs of digits
  temp = abs_value
  pairs = []
  while temp > 0
    pairs.unshift(temp % 100)
    temp /= 100
  end

  # Handle single digit numbers specially
  if digit_count == 1
    pairs = [abs_value]
  end

  # Encode each pair
  pairs.each { |pair| result << cvt_table[pair] }

  # Add terminator for negative numbers
  result << NEG_MNTSSA_END if is_negative

  result.pack('C*')
end