class Avro::LogicalTypes::BytesDecimal
Logical type to handle arbitrary-precision decimals using byte array.
The byte array contains the two’s-complement representation of the unscaled integer value in big-endian byte order.
Constants
- ERROR_INSUFFICIENT_PRECISION
Messages for exceptions
- ERROR_ROUNDING_NECESSARY
- ERROR_VALUE_MUST_BE_NUMERIC
- PACK_UNSIGNED_CHARS
The pattern used to pack up the byte array (8 bit unsigned integer/char)
- TEN
The number 10 as BigDecimal
Attributes
@return [Integer] The number of total digits supported by the decimal
@return [Integer] The number of fractional digits
Public Class Methods
Build a new decimal logical type
@param schema [Avro::Schema]
The schema defining precision and scale for the conversion
Avro::LogicalTypes::LogicalTypeWithSchema::new
# File lib/avro/logical_types.rb 98 def initialize(schema) 99 super 100 101 @scale = schema.scale.to_i 102 @precision = schema.precision.to_i 103 @factor = TEN ** @scale 104 end
Public Instance Methods
Decode a byte array (in form of a string) into a BigDecimal of the given precision and scale
@param stream [String]
The byte array to decode
@return [BigDecimal]
# File lib/avro/logical_types.rb 132 def decode(stream) 133 from_byte_array(stream) / @factor 134 end
Encode the provided value into a byte array
@param value [BigDecimal, Float, Integer]
The numeric value to encode
@raise [ArgumentError]
If the provided value is not a numeric type
@raise [RangeError]
If the provided value has a scale higher than the schema permits, or does not fit into the schema's precision
# File lib/avro/logical_types.rb 118 def encode(value) 119 raise ArgumentError, ERROR_VALUE_MUST_BE_NUMERIC unless value.is_a?(Numeric) 120 121 to_byte_array(unscaled_value(value.to_d)).pack(PACK_UNSIGNED_CHARS).freeze 122 end
Private Instance Methods
Convert the provided stream of bytes into the unscaled value
@param stream [String]
The stream of bytes to convert
@return [Integer]
# File lib/avro/logical_types.rb 145 def from_byte_array(stream) 146 bytes = stream.bytes 147 positive = bytes.first[7].zero? 148 total = 0 149 150 bytes.each_with_index do |value, ix| 151 total += (positive ? value : (value ^ 0xff)) << (bytes.length - ix - 1) * 8 152 end 153 154 return total if positive 155 156 -(total + 1) 157 end
Convert the provided number into its two’s complement representation in network order (big endian).
@param number [Integer]
The number to convert
@return [Array<Integer>]
The byte array in network order
# File lib/avro/logical_types.rb 168 def to_byte_array(number) 169 [].tap do |result| 170 loop do 171 result.unshift(number & 0xff) 172 number >>= 8 173 174 break if (number == 0 || number == -1) && (result.first[7] == number[7]) 175 end 176 end 177 end
Get the unscaled value from a BigDecimal considering the schema’s scale
@param decimal [BigDecimal]
The decimal to get the unscaled value from
@return [Integer]
# File lib/avro/logical_types.rb 186 def unscaled_value(decimal) 187 details = decimal.split 188 length = details[1].length 189 190 fractional_part = length - details[3] 191 raise RangeError, ERROR_ROUNDING_NECESSARY if fractional_part > scale 192 193 if length > precision || (length - fractional_part) > (precision - scale) 194 raise RangeError, ERROR_INSUFFICIENT_PRECISION 195 end 196 197 (decimal * @factor).to_i 198 end