Add support for BigDecimal and BigInt
This commit is contained in:
		
							parent
							
								
									bb4d3e2b71
								
							
						
					
					
						commit
						7fc53ab114
					
				
					 4 changed files with 50 additions and 7 deletions
				
			
		
							
								
								
									
										3
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
{
 | 
			
		||||
    "crystal-lang.mainFile": "${workspaceRoot}/src/cbor.cr"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ and the values are encoded in the respective Crystal types:
 | 
			
		|||
 | 
			
		||||
- `Time`
 | 
			
		||||
- `BigInt`
 | 
			
		||||
- `BigFloat`
 | 
			
		||||
- `BigDecimal`
 | 
			
		||||
 | 
			
		||||
## Development
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,9 @@ describe "CBOR helpers on basic types" do
 | 
			
		|||
      {Tuple(Int8, Int8), Bytes[0x82, 0x01, 0x02], {1_i8, 2_i8}},
 | 
			
		||||
      {NamedTuple(a: UInt8, b: Array(UInt8)), Bytes[0xa2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x82, 0x02, 0x03], {a: 1_u8, b: [2_u8, 3_u8]}},
 | 
			
		||||
      {BigInt, Bytes[0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], BigInt.new("18446744073709551616")},
 | 
			
		||||
      {BigInt, Bytes[0x18, 0x18], BigInt.new(24)}, # BigInt from int
 | 
			
		||||
      {BigDecimal, Bytes[0xc4, 0x82, 0x21, 0x19, 0x6a, 0xb3], BigDecimal.new(273.15)},
 | 
			
		||||
      {BigDecimal, Bytes[0xc5, 0x82, 0x20, 0x03], BigDecimal.new(1.5)},
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    tests.each do |tt|
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -149,12 +149,20 @@ def Time.new(decoder : CBOR::Decoder)
 | 
			
		|||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Reads the CBOR value as a BigInt. The value must be surrounded by a tag with
 | 
			
		||||
# value 2 (positive) or 3 (negative).
 | 
			
		||||
# Reads the CBOR value as a BigInt.
 | 
			
		||||
# If the next token is an integer, then the integer will be transformed as a
 | 
			
		||||
# BigInt, otherwhise the value must be surrounded by a tag with value 2
 | 
			
		||||
# (positive) or 3 (negative).
 | 
			
		||||
def BigInt.new(decoder : CBOR::Decoder)
 | 
			
		||||
  case tag = decoder.read_tag
 | 
			
		||||
  when CBOR::Tag::PositiveBigNum,
 | 
			
		||||
       CBOR::Tag::NegativeBigNum
 | 
			
		||||
  case token = decoder.current_token
 | 
			
		||||
  when CBOR::Token::TagT
 | 
			
		||||
    decoder.finish_token!
 | 
			
		||||
 | 
			
		||||
    tag = token.value
 | 
			
		||||
    unless tag == CBOR::Tag::PositiveBigNum || tag == CBOR::Tag::NegativeBigNum
 | 
			
		||||
      raise CBOR::ParseError.new("Expected tag to have value 2 or 3, got #{tag.value}")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    big = new(decoder.read_bytes.hexstring, 16)
 | 
			
		||||
 | 
			
		||||
    if tag == CBOR::Tag::NegativeBigNum
 | 
			
		||||
| 
						 | 
				
			
			@ -163,8 +171,37 @@ def BigInt.new(decoder : CBOR::Decoder)
 | 
			
		|||
    end
 | 
			
		||||
 | 
			
		||||
    big
 | 
			
		||||
  when CBOR::Token::IntT
 | 
			
		||||
    decoder.finish_token!
 | 
			
		||||
    new(token.value)
 | 
			
		||||
  else
 | 
			
		||||
    raise CBOR::ParseError.new("Expected tag to have value 2 or 3, got #{tag.value}")
 | 
			
		||||
    decoder.unexpected_token(token, "IntT or TagT")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Reads the CBOR value as a BigDecimal.
 | 
			
		||||
# If the next token is a flaot, then it'll be transformed to a BigDecimal,
 | 
			
		||||
# otherwhise the value must be correctly tagged with value 4 (decimal fraction)
 | 
			
		||||
# or 5 (big float).
 | 
			
		||||
def BigDecimal.new(decoder : CBOR::Decoder)
 | 
			
		||||
  case token = decoder.current_token
 | 
			
		||||
  when CBOR::Token::TagT
 | 
			
		||||
    decoder.finish_token!
 | 
			
		||||
 | 
			
		||||
    tag = token.value
 | 
			
		||||
    unless tag == CBOR::Tag::Decimal || tag == CBOR::Tag::BigFloat
 | 
			
		||||
      raise CBOR::ParseError.new("Expected tag to have value 4 or 5, got #{tag.value}")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    exponent, matissa = Tuple(Int128, BigInt).new(decoder)
 | 
			
		||||
 | 
			
		||||
    base = tag == CBOR::Tag::Decimal ? 10.0 : 2.0
 | 
			
		||||
    e = base**exponent
 | 
			
		||||
    new(matissa) * new(e)
 | 
			
		||||
  when CBOR::Token::FloatT
 | 
			
		||||
    new(token.value)
 | 
			
		||||
  else
 | 
			
		||||
    decoder.unexpected_token(token, "FloatT or TagT")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue