Merge pull request #250 from stevenroose/no-strason
Remove Decimal and replace strason with serde_json
This commit is contained in:
commit
b2727b6ebe
10
Cargo.toml
10
Cargo.toml
|
@ -16,7 +16,6 @@ path = "src/lib.rs"
|
|||
|
||||
[features]
|
||||
fuzztarget = ["secp256k1/fuzztarget", "bitcoin_hashes/fuzztarget"]
|
||||
serde-decimal = ["use-serde", "strason"]
|
||||
unstable = []
|
||||
use-serde = ["serde", "bitcoin_hashes/serde"]
|
||||
|
||||
|
@ -32,15 +31,6 @@ version = "1"
|
|||
features = ["derive"]
|
||||
optional = true
|
||||
|
||||
[dependencies.serde_test]
|
||||
version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.strason]
|
||||
version = "0.4"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.hex]
|
||||
version = "=0.3.2"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
FEATURES="bitcoinconsensus use-serde serde-decimal"
|
||||
FEATURES="bitcoinconsensus use-serde"
|
||||
|
||||
# Use toolchain if explicitly specified
|
||||
if [ -n "$TOOLCHAIN" ]
|
||||
|
|
|
@ -14,7 +14,7 @@ honggfuzz_fuzz = ["honggfuzz"]
|
|||
[dependencies]
|
||||
honggfuzz = { version = "0.5", optional = true }
|
||||
afl = { version = "0.4", optional = true }
|
||||
bitcoin = { path = "..", features = ["fuzztarget", "serde-decimal"] }
|
||||
bitcoin = { path = "..", features = ["fuzztarget"] }
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
|
@ -40,14 +40,6 @@ path = "fuzz_targets/deserialize_address.rs"
|
|||
name = "deserialize_amount"
|
||||
path = "fuzz_targets/deserialize_amount.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "deserialize_decimal"
|
||||
path = "fuzz_targets/deserialize_decimal.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "deserialize_udecimal"
|
||||
path = "fuzz_targets/deserialize_udecimal.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "outpoint_string"
|
||||
path = "fuzz_targets/outpoint_string.rs"
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
extern crate bitcoin;
|
||||
use std::str::FromStr;
|
||||
fn do_test(data: &[u8]) {
|
||||
let data_str = String::from_utf8_lossy(data);
|
||||
let dec = match bitcoin::util::decimal::Decimal::from_str(&data_str) {
|
||||
Ok(dec) => dec,
|
||||
Err(_) => return,
|
||||
};
|
||||
let dec_roundtrip = match bitcoin::util::decimal::Decimal::from_str(&dec.to_string()) {
|
||||
Ok(dec) => dec,
|
||||
Err(_) => return,
|
||||
};
|
||||
assert_eq!(dec, dec_roundtrip);
|
||||
}
|
||||
|
||||
#[cfg(feature = "afl")]
|
||||
#[macro_use] extern crate afl;
|
||||
#[cfg(feature = "afl")]
|
||||
fn main() {
|
||||
fuzz!(|data| {
|
||||
do_test(&data);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "honggfuzz")]
|
||||
#[macro_use] extern crate honggfuzz;
|
||||
#[cfg(feature = "honggfuzz")]
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data| {
|
||||
do_test(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
|
||||
let mut b = 0;
|
||||
for (idx, c) in hex.as_bytes().iter().enumerate() {
|
||||
b <<= 4;
|
||||
match *c {
|
||||
b'A'...b'F' => b |= c - b'A' + 10,
|
||||
b'a'...b'f' => b |= c - b'a' + 10,
|
||||
b'0'...b'9' => b |= c - b'0',
|
||||
_ => panic!("Bad hex"),
|
||||
}
|
||||
if (idx & 1) == 1 {
|
||||
out.push(b);
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_crash() {
|
||||
let mut a = Vec::new();
|
||||
extend_vec_from_hex("00000000", &mut a);
|
||||
super::do_test(&a);
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
extern crate bitcoin;
|
||||
use std::str::FromStr;
|
||||
fn do_test(data: &[u8]) {
|
||||
let data_str = String::from_utf8_lossy(data);
|
||||
let dec = match bitcoin::util::decimal::UDecimal::from_str(&data_str) {
|
||||
Ok(dec) => dec,
|
||||
Err(_) => return,
|
||||
};
|
||||
let dec_roundtrip = match bitcoin::util::decimal::UDecimal::from_str(&dec.to_string()) {
|
||||
Ok(dec) => dec,
|
||||
Err(_) => return,
|
||||
};
|
||||
assert_eq!(dec, dec_roundtrip);
|
||||
}
|
||||
|
||||
#[cfg(feature = "afl")]
|
||||
#[macro_use] extern crate afl;
|
||||
#[cfg(feature = "afl")]
|
||||
fn main() {
|
||||
fuzz!(|data| {
|
||||
do_test(&data);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "honggfuzz")]
|
||||
#[macro_use] extern crate honggfuzz;
|
||||
#[cfg(feature = "honggfuzz")]
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data| {
|
||||
do_test(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
|
||||
let mut b = 0;
|
||||
for (idx, c) in hex.as_bytes().iter().enumerate() {
|
||||
b <<= 4;
|
||||
match *c {
|
||||
b'A'...b'F' => b |= c - b'A' + 10,
|
||||
b'a'...b'f' => b |= c - b'a' + 10,
|
||||
b'0'...b'9' => b |= c - b'0',
|
||||
_ => panic!("Bad hex"),
|
||||
}
|
||||
if (idx & 1) == 1 {
|
||||
out.push(b);
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_crash() {
|
||||
let mut a = Vec::new();
|
||||
extend_vec_from_hex("00000000", &mut a);
|
||||
super::do_test(&a);
|
||||
}
|
||||
}
|
|
@ -805,15 +805,14 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "serde", feature = "strason"))]
|
||||
#[cfg(feature = "serde")]
|
||||
fn script_json_serialize() {
|
||||
use strason::Json;
|
||||
use serde_json;
|
||||
|
||||
let original = hex_script!("827651a0698faaa9a8a7a687");
|
||||
let json = Json::from_serialize(&original).unwrap();
|
||||
assert_eq!(json.to_bytes(), b"\"827651a0698faaa9a8a7a687\"");
|
||||
assert_eq!(json.string(), Some("827651a0698faaa9a8a7a687"));
|
||||
let des = json.into_deserialize().unwrap();
|
||||
let json = serde_json::to_value(&original).unwrap();
|
||||
assert_eq!(json, serde_json::Value::String("827651a0698faaa9a8a7a687".to_owned()));
|
||||
let des = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(original, des);
|
||||
}
|
||||
|
||||
|
|
|
@ -598,9 +598,6 @@ impl SigHashType {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(all(feature = "serde", feature = "strason"))]
|
||||
use strason::Json;
|
||||
|
||||
use super::{OutPoint, ParseOutPointError, Transaction, TxIn};
|
||||
|
||||
use std::str::FromStr;
|
||||
|
@ -771,14 +768,11 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "serde", feature = "strason"))]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_txn_encode_decode() {
|
||||
let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
|
||||
let tx: Transaction = deserialize(&hex_tx).unwrap();
|
||||
|
||||
let encoded = Json::from_serialize(&tx).unwrap();
|
||||
let decoded = encoded.into_deserialize().unwrap();
|
||||
assert_eq!(tx, decoded);
|
||||
serde_round_trip!(tx);
|
||||
}
|
||||
|
||||
fn run_test_sighash(tx: &str, script: &str, input_index: usize, hash_type: i32, expected_result: &str) {
|
||||
|
@ -795,15 +789,12 @@ mod tests {
|
|||
// Test decoding transaction `4be105f158ea44aec57bf12c5817d073a712ab131df6f37786872cfc70734188`
|
||||
// from testnet, which is the first BIP144-encoded transaction I encountered.
|
||||
#[test]
|
||||
#[cfg(all(feature = "serde", feature = "strason"))]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_segwit_tx_decode() {
|
||||
let hex_tx = hex_bytes("010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff3603da1b0e00045503bd5704c7dd8a0d0ced13bb5785010800000000000a636b706f6f6c122f4e696e6a61506f6f6c2f5345475749542fffffffff02b4e5a212000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9edf91c46b49eb8a29089980f02ee6b57e7d63d33b18b4fddac2bcd7db2a39837040120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
|
||||
let tx: Transaction = deserialize(&hex_tx).unwrap();
|
||||
assert_eq!(tx.get_weight(), 780);
|
||||
|
||||
let encoded = Json::from_serialize(&tx).unwrap();
|
||||
let decoded = encoded.into_deserialize().unwrap();
|
||||
assert_eq!(tx, decoded);
|
||||
serde_round_trip!(tx);
|
||||
|
||||
let consensus_encoded = serialize(&tx);
|
||||
assert_eq!(consensus_encoded, hex_tx);
|
||||
|
|
|
@ -49,7 +49,6 @@ extern crate hex;
|
|||
extern crate rand;
|
||||
extern crate secp256k1;
|
||||
#[cfg(feature = "serde")] extern crate serde;
|
||||
#[cfg(feature = "strason")] extern crate strason;
|
||||
#[cfg(all(test, feature = "serde"))] #[macro_use] extern crate serde_derive; // for 1.22.0 compat
|
||||
#[cfg(all(test, feature = "serde"))] extern crate serde_json;
|
||||
#[cfg(all(test, feature = "serde"))] extern crate serde_test;
|
||||
|
@ -84,5 +83,3 @@ pub use util::amount::SignedAmount;
|
|||
pub use util::hash::BitcoinHash;
|
||||
pub use util::key::PrivateKey;
|
||||
pub use util::key::PublicKey;
|
||||
pub use util::decimal::Decimal;
|
||||
pub use util::decimal::UDecimal;
|
||||
|
|
|
@ -16,15 +16,14 @@
|
|||
//!
|
||||
//! Internal macros used for unit tests
|
||||
|
||||
#[cfg(all(feature = "serde", feature = "strason"))]
|
||||
#[cfg(feature = "serde")]
|
||||
macro_rules! serde_round_trip (
|
||||
($var:expr) => ({
|
||||
use $crate::strason::Json;
|
||||
use serde_json;
|
||||
|
||||
let start = $var;
|
||||
let encoded = Json::from_serialize(&start).unwrap();
|
||||
let decoded = encoded.into_deserialize().unwrap();
|
||||
assert_eq!(start, decoded);
|
||||
let encoded = serde_json::to_value(&$var).unwrap();
|
||||
let decoded = serde_json::from_value(encoded).unwrap();
|
||||
assert_eq!($var, decoded);
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -500,14 +500,14 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "serde", feature = "strason"))]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_json_serialize() {
|
||||
use strason::Json;
|
||||
use serde_json;
|
||||
|
||||
let addr = Address::from_str("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM").unwrap();
|
||||
let json = Json::from_serialize(&addr).unwrap();
|
||||
assert_eq!(json.string(), Some("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"));
|
||||
let into: Address = json.into_deserialize().unwrap();
|
||||
let json = serde_json::to_value(&addr).unwrap();
|
||||
assert_eq!(json, serde_json::Value::String("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM".to_owned()));
|
||||
let into: Address = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(addr.to_string(), into.to_string());
|
||||
assert_eq!(
|
||||
into.script_pubkey(),
|
||||
|
@ -515,9 +515,9 @@ mod tests {
|
|||
);
|
||||
|
||||
let addr = Address::from_str("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k").unwrap();
|
||||
let json = Json::from_serialize(&addr).unwrap();
|
||||
assert_eq!(json.string(), Some("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"));
|
||||
let into: Address = json.into_deserialize().unwrap();
|
||||
let json = serde_json::to_value(&addr).unwrap();
|
||||
assert_eq!(json, serde_json::Value::String("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k".to_owned()));
|
||||
let into: Address = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(addr.to_string(), into.to_string());
|
||||
assert_eq!(
|
||||
into.script_pubkey(),
|
||||
|
@ -525,9 +525,9 @@ mod tests {
|
|||
);
|
||||
|
||||
let addr = Address::from_str("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7").unwrap();
|
||||
let json = Json::from_serialize(&addr).unwrap();
|
||||
assert_eq!(json.string(), Some("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"));
|
||||
let into: Address = json.into_deserialize().unwrap();
|
||||
let json = serde_json::to_value(&addr).unwrap();
|
||||
assert_eq!(json, serde_json::Value::String("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7".to_owned()));
|
||||
let into: Address = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(addr.to_string(), into.to_string());
|
||||
assert_eq!(
|
||||
into.script_pubkey(),
|
||||
|
@ -535,9 +535,9 @@ mod tests {
|
|||
);
|
||||
|
||||
let addr = Address::from_str("bcrt1q2nfxmhd4n3c8834pj72xagvyr9gl57n5r94fsl").unwrap();
|
||||
let json = Json::from_serialize(&addr).unwrap();
|
||||
assert_eq!(json.string(), Some("bcrt1q2nfxmhd4n3c8834pj72xagvyr9gl57n5r94fsl"));
|
||||
let into: Address = json.into_deserialize().unwrap();
|
||||
let json = serde_json::to_value(&addr).unwrap();
|
||||
assert_eq!(json, serde_json::Value::String("bcrt1q2nfxmhd4n3c8834pj72xagvyr9gl57n5r94fsl".to_owned()));
|
||||
let into: Address = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(addr.to_string(), into.to_string());
|
||||
assert_eq!(
|
||||
into.script_pubkey(),
|
||||
|
|
|
@ -988,7 +988,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "serde", feature = "strason"))]
|
||||
#[cfg(feature = "serde")]
|
||||
pub fn encode_decode_childnumber() {
|
||||
serde_round_trip!(ChildNumber::from_normal_idx(0).unwrap());
|
||||
serde_round_trip!(ChildNumber::from_normal_idx(1).unwrap());
|
||||
|
|
|
@ -1,604 +0,0 @@
|
|||
// Rust Bitcoin Library
|
||||
// Written in 2014 by
|
||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all
|
||||
// copyright and related and neighboring rights to this software to
|
||||
// the public domain worldwide. This software is distributed without
|
||||
// any warranty.
|
||||
//
|
||||
// You should have received a copy of the CC0 Public Domain Dedication
|
||||
// along with this software.
|
||||
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
//
|
||||
|
||||
//! Floating-point decimal type
|
||||
//!
|
||||
//! `i64`-based floating-point decimal type designed to hold Bitcoin
|
||||
//! amounts. For satoshi amounts (8 decimal places) the maximum
|
||||
//! amounts that can be represented is ~92.25bn, well over the 21m
|
||||
//! maximum number of bitcoin in existence. Be aware that some
|
||||
//! altcoins with different granularity may require a wider type.
|
||||
//!
|
||||
|
||||
use std::{fmt, ops};
|
||||
#[cfg(feature = "serde-decimal")] use std::error;
|
||||
#[cfg(feature = "serde-decimal")] use std::str::FromStr;
|
||||
|
||||
#[cfg(feature = "serde-decimal")] use serde;
|
||||
#[cfg(feature = "serde-decimal")] use strason::{self, Json};
|
||||
|
||||
/// A fixed-point decimal type
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord)]
|
||||
pub struct Decimal {
|
||||
mantissa: i64,
|
||||
exponent: usize,
|
||||
}
|
||||
|
||||
/// Unsigned fixed-point decimal type
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord)]
|
||||
pub struct UDecimal {
|
||||
mantissa: u64,
|
||||
exponent: usize,
|
||||
}
|
||||
|
||||
impl PartialEq<Decimal> for Decimal {
|
||||
fn eq(&self, other: &Decimal) -> bool {
|
||||
use std::cmp::max;
|
||||
let exp = max(self.exponent(), other.exponent());
|
||||
self.integer_value(exp) == other.integer_value(exp)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Decimal> for Decimal {
|
||||
fn partial_cmp(&self, other: &Decimal) -> Option<::std::cmp::Ordering> {
|
||||
use std::cmp::max;
|
||||
let exp = max(self.exponent(), other.exponent());
|
||||
self.integer_value(exp).partial_cmp(&other.integer_value(exp))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Decimal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let ten = 10i64.pow(self.exponent as u32);
|
||||
let int_part = self.mantissa / ten;
|
||||
let dec_part = (self.mantissa % ten).abs();
|
||||
if int_part == 0 && self.mantissa < 0 {
|
||||
write!(f, "-{}.{:02$}", int_part, dec_part, self.exponent)
|
||||
} else {
|
||||
write!(f, "{}.{:02$}", int_part, dec_part, self.exponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add for Decimal {
|
||||
type Output = Decimal;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Decimal) -> Decimal {
|
||||
if self.exponent > other.exponent {
|
||||
Decimal {
|
||||
mantissa: other.mantissa * 10i64.pow((self.exponent - other.exponent) as u32) + self.mantissa,
|
||||
exponent: self.exponent
|
||||
}
|
||||
} else {
|
||||
Decimal {
|
||||
mantissa: self.mantissa * 10i64.pow((other.exponent - self.exponent) as u32) + other.mantissa,
|
||||
exponent: other.exponent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Neg for Decimal {
|
||||
type Output = Decimal;
|
||||
#[inline]
|
||||
fn neg(self) -> Decimal { Decimal { mantissa: -self.mantissa, exponent: self.exponent } }
|
||||
}
|
||||
|
||||
impl ops::Sub for Decimal {
|
||||
type Output = Decimal;
|
||||
#[inline]
|
||||
fn sub(self, other: Decimal) -> Decimal { self + (-other) }
|
||||
}
|
||||
|
||||
impl Decimal {
|
||||
/// Creates a new Decimal
|
||||
pub fn new(mantissa: i64, exponent: usize) -> Decimal {
|
||||
Decimal {
|
||||
mantissa: mantissa,
|
||||
exponent: exponent
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the mantissa
|
||||
#[inline]
|
||||
pub fn mantissa(&self) -> i64 { self.mantissa }
|
||||
/// Returns the exponent
|
||||
#[inline]
|
||||
pub fn exponent(&self) -> usize { self.exponent }
|
||||
|
||||
/// Get the decimal's value in an integer type, by multiplying
|
||||
/// by some power of ten to ensure the returned value is 10 **
|
||||
/// `exponent` types the actual value.
|
||||
pub fn integer_value(&self, exponent: usize) -> i64 {
|
||||
if exponent < self.exponent {
|
||||
self.mantissa / 10i64.pow((self.exponent - exponent) as u32)
|
||||
} else {
|
||||
self.mantissa * 10i64.pow((exponent - self.exponent) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not the number is nonnegative
|
||||
#[inline]
|
||||
pub fn nonnegative(&self) -> bool { self.mantissa >= 0 }
|
||||
|
||||
// Converts a JSON number to a Decimal previously parsed by strason
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
fn parse_decimal(s: &str) -> Result<Decimal, ParseDecimalError> {
|
||||
// We know this will be a well-formed Json number, so we can
|
||||
// be pretty lax about parsing
|
||||
let mut negative = false;
|
||||
let mut past_dec = false;
|
||||
let mut exponent = 0;
|
||||
let mut mantissa = 0i64;
|
||||
|
||||
for b in s.as_bytes() {
|
||||
match *b {
|
||||
b'-' => { negative = true; }
|
||||
b'0'...b'9' => {
|
||||
match 10i64.checked_mul(mantissa) {
|
||||
None => return Err(ParseDecimalError::TooBig),
|
||||
Some(n) => {
|
||||
match n.checked_add((b - b'0') as i64) {
|
||||
None => return Err(ParseDecimalError::TooBig),
|
||||
Some(n) => mantissa = n,
|
||||
}
|
||||
}
|
||||
}
|
||||
if past_dec {
|
||||
exponent += 1;
|
||||
if exponent > 18 {
|
||||
return Err(ParseDecimalError::TooBig);
|
||||
}
|
||||
}
|
||||
}
|
||||
b'.' => { past_dec = true; }
|
||||
_ => { /* whitespace or something, just ignore it */ }
|
||||
}
|
||||
}
|
||||
if negative { mantissa *= -1; }
|
||||
Ok(Decimal {
|
||||
mantissa: mantissa,
|
||||
exponent: exponent,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
impl FromStr for Decimal {
|
||||
type Err = ParseDecimalError;
|
||||
|
||||
/// Parses a `Decimal` from the given amount string.
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Json::from_str(s)?
|
||||
.num()
|
||||
.ok_or(ParseDecimalError::NotANumber)
|
||||
.and_then(Decimal::parse_decimal)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
impl<'de> serde::Deserialize<'de> for Decimal {
|
||||
/// Deserialize a `Decimal`.
|
||||
///
|
||||
/// This type is deserialized through [`strason`][1] for the same reason as
|
||||
/// it's explained in the `Serialize` implementation.
|
||||
///
|
||||
/// [1]: https://github.com/apoelstra/strason
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de;
|
||||
|
||||
Json::deserialize(deserializer)?
|
||||
.num()
|
||||
.ok_or(de::Error::custom("expected decimal, got non-numeric"))
|
||||
.and_then(|s| Decimal::parse_decimal(s).map_err(de::Error::custom))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
impl serde::Serialize for Decimal {
|
||||
/// Serialize a `Decimal`.
|
||||
///
|
||||
/// This type is serialized through [`strason`][1] since it will not lose
|
||||
/// precision (when serializing to [`strason`][1] itself, the value will be
|
||||
/// passed through; otherwise it will be encoded as a string).
|
||||
///
|
||||
/// [1]: https://github.com/apoelstra/strason
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let json = Json::from_str(&self.to_string()).unwrap();
|
||||
json.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<UDecimal> for UDecimal {
|
||||
fn eq(&self, other: &UDecimal) -> bool {
|
||||
use std::cmp::max;
|
||||
let exp = max(self.exponent(), other.exponent());
|
||||
self.integer_value(exp) == other.integer_value(exp)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<UDecimal> for UDecimal {
|
||||
fn partial_cmp(&self, other: &UDecimal) -> Option<::std::cmp::Ordering> {
|
||||
use std::cmp::max;
|
||||
let exp = max(self.exponent(), other.exponent());
|
||||
self.integer_value(exp).partial_cmp(&other.integer_value(exp))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UDecimal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let ten = 10u64.pow(self.exponent as u32);
|
||||
let int_part = self.mantissa / ten;
|
||||
let dec_part = self.mantissa % ten;
|
||||
write!(f, "{}.{:02$}", int_part, dec_part, self.exponent)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add for UDecimal {
|
||||
type Output = UDecimal;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: UDecimal) -> UDecimal {
|
||||
if self.exponent > other.exponent {
|
||||
UDecimal {
|
||||
mantissa: other.mantissa * 10u64.pow((self.exponent - other.exponent) as u32) + self.mantissa,
|
||||
exponent: self.exponent
|
||||
}
|
||||
} else {
|
||||
UDecimal {
|
||||
mantissa: self.mantissa * 10u64.pow((other.exponent - self.exponent) as u32) + other.mantissa,
|
||||
exponent: other.exponent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UDecimal {
|
||||
/// Creates a new Decimal
|
||||
pub fn new(mantissa: u64, exponent: usize) -> UDecimal {
|
||||
UDecimal {
|
||||
mantissa: mantissa,
|
||||
exponent: exponent
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the mantissa
|
||||
#[inline]
|
||||
pub fn mantissa(&self) -> u64 { self.mantissa }
|
||||
/// Returns the exponent
|
||||
#[inline]
|
||||
pub fn exponent(&self) -> usize { self.exponent }
|
||||
|
||||
/// Get the decimal's value in an integer type, by multiplying
|
||||
/// by some power of ten to ensure the returned value is 10 **
|
||||
/// `exponent` types the actual value.
|
||||
pub fn integer_value(&self, exponent: usize) -> u64 {
|
||||
if exponent < self.exponent {
|
||||
self.mantissa / 10u64.pow((self.exponent - exponent) as u32)
|
||||
} else {
|
||||
self.mantissa * 10u64.pow((exponent - self.exponent) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a JSON number to a Decimal previously parsed by strason
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
fn parse_udecimal(s: &str) -> Result<UDecimal, ParseDecimalError> {
|
||||
// We know this will be a well-formed Json number, so we can
|
||||
// be pretty lax about parsing
|
||||
let mut past_dec = false;
|
||||
let mut exponent = 0;
|
||||
let mut mantissa = 0u64;
|
||||
|
||||
for b in s.as_bytes() {
|
||||
match *b {
|
||||
b'0'...b'9' => {
|
||||
match 10u64.checked_mul(mantissa) {
|
||||
None => return Err(ParseDecimalError::TooBig),
|
||||
Some(n) => {
|
||||
match n.checked_add((b - b'0') as u64) {
|
||||
None => return Err(ParseDecimalError::TooBig),
|
||||
Some(n) => mantissa = n,
|
||||
}
|
||||
}
|
||||
}
|
||||
if past_dec {
|
||||
exponent += 1;
|
||||
if exponent > 18 {
|
||||
return Err(ParseDecimalError::TooBig);
|
||||
}
|
||||
}
|
||||
}
|
||||
b'.' => { past_dec = true; }
|
||||
_ => { /* whitespace or something, just ignore it */ }
|
||||
}
|
||||
}
|
||||
Ok(UDecimal {
|
||||
mantissa: mantissa,
|
||||
exponent: exponent,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
impl FromStr for UDecimal {
|
||||
type Err = ParseDecimalError;
|
||||
|
||||
/// Parses a `UDecimal` from the given amount string.
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Json::from_str(s)?
|
||||
.num()
|
||||
.ok_or(ParseDecimalError::NotANumber)
|
||||
.and_then(UDecimal::parse_udecimal)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
impl<'de> serde::Deserialize<'de> for UDecimal {
|
||||
/// Deserialize an `UDecimal`.
|
||||
///
|
||||
/// This type is deserialized through [`strason`][1] for the same reason as
|
||||
/// it's explained in the `Serialize` implementation.
|
||||
///
|
||||
/// [1]: https://github.com/apoelstra/strason
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::de;
|
||||
|
||||
Json::deserialize(deserializer)?
|
||||
.num()
|
||||
.ok_or(de::Error::custom("expected decimal, got non-numeric"))
|
||||
.and_then(|s| UDecimal::parse_udecimal(s).map_err(de::Error::custom))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
impl serde::Serialize for UDecimal {
|
||||
/// Serialize an `UDecimal`.
|
||||
///
|
||||
/// This type is serialized through [`strason`][1] since it will not lose
|
||||
/// precision (when serializing to [`strason`][1] itself, the value will be
|
||||
/// passed through; otherwise it will be encoded as a string).
|
||||
///
|
||||
/// [1]: https://github.com/apoelstra/strason
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let json = Json::from_str(&self.to_string()).unwrap();
|
||||
json.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that occur during `Decimal`/`UDecimal` parsing.
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
#[derive(Debug)]
|
||||
pub enum ParseDecimalError {
|
||||
/// An error ocurred while parsing the JSON number.
|
||||
Json(strason::Error),
|
||||
/// Not a number.
|
||||
NotANumber,
|
||||
/// The number is too big to fit in a `Decimal` or `UDecimal`.
|
||||
TooBig,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
#[doc(hidden)]
|
||||
impl From<strason::Error> for ParseDecimalError {
|
||||
fn from(e: strason::Error) -> ParseDecimalError {
|
||||
ParseDecimalError::Json(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
impl fmt::Display for ParseDecimalError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ParseDecimalError::Json(ref e) => fmt::Display::fmt(e, fmt),
|
||||
ParseDecimalError::NotANumber => fmt.write_str("not a valid JSON number"),
|
||||
ParseDecimalError::TooBig => fmt.write_str("number is too big"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
impl error::Error for ParseDecimalError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
ParseDecimalError::Json(ref e) => e.description(),
|
||||
ParseDecimalError::NotANumber => "not a valid JSON number",
|
||||
ParseDecimalError::TooBig => "number is too big",
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
ParseDecimalError::Json(ref e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
use strason::Json;
|
||||
|
||||
#[test]
|
||||
fn integer_value() {
|
||||
let d = Decimal::new(12345678, 4);
|
||||
assert_eq!(d.mantissa(), 12345678);
|
||||
assert_eq!(d.exponent(), 4);
|
||||
|
||||
assert_eq!(d.integer_value(0), 1234);
|
||||
assert_eq!(d.integer_value(1), 12345);
|
||||
assert_eq!(d.integer_value(2), 123456);
|
||||
assert_eq!(d.integer_value(3), 1234567);
|
||||
assert_eq!(d.integer_value(4), 12345678);
|
||||
assert_eq!(d.integer_value(5), 123456780);
|
||||
assert_eq!(d.integer_value(6), 1234567800);
|
||||
assert_eq!(d.integer_value(7), 12345678000);
|
||||
assert_eq!(d.integer_value(8), 123456780000);
|
||||
|
||||
let u = UDecimal::new(12345678, 4);
|
||||
assert_eq!(u.mantissa(), 12345678);
|
||||
assert_eq!(u.exponent(), 4);
|
||||
|
||||
assert_eq!(u.integer_value(0), 1234);
|
||||
assert_eq!(u.integer_value(1), 12345);
|
||||
assert_eq!(u.integer_value(2), 123456);
|
||||
assert_eq!(u.integer_value(3), 1234567);
|
||||
assert_eq!(u.integer_value(4), 12345678);
|
||||
assert_eq!(u.integer_value(5), 123456780);
|
||||
assert_eq!(u.integer_value(6), 1234567800);
|
||||
assert_eq!(u.integer_value(7), 12345678000);
|
||||
assert_eq!(u.integer_value(8), 123456780000);
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
macro_rules! deserialize_round_trip(
|
||||
($dec:expr, $s:expr) => ({
|
||||
let d = $dec;
|
||||
let encoded = Json::from_serialize(&d).unwrap();
|
||||
assert_eq!(encoded, Json::from_reader(&$s[..]).unwrap());
|
||||
assert_eq!(encoded.to_bytes(), &$s[..]);
|
||||
|
||||
// hack to force type inference
|
||||
let mut decoded_res = encoded.into_deserialize();
|
||||
if false { decoded_res = Ok($dec); }
|
||||
let decoded = decoded_res.unwrap();
|
||||
assert_eq!(decoded, d);
|
||||
})
|
||||
);
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
fn deserialize() {
|
||||
deserialize_round_trip!(Decimal::new(0, 0), b"0.0");
|
||||
deserialize_round_trip!(UDecimal::new(0, 0), b"0.0");
|
||||
|
||||
deserialize_round_trip!(Decimal::new(123456789001, 8), b"1234.56789001");
|
||||
deserialize_round_trip!(UDecimal::new(123456789001, 8), b"1234.56789001");
|
||||
deserialize_round_trip!(Decimal::new(-123456789001, 8), b"-1234.56789001");
|
||||
deserialize_round_trip!(Decimal::new(123456789001, 1), b"12345678900.1");
|
||||
deserialize_round_trip!(UDecimal::new(123456789001, 1), b"12345678900.1");
|
||||
deserialize_round_trip!(Decimal::new(-123456789001, 1), b"-12345678900.1");
|
||||
deserialize_round_trip!(Decimal::new(123456789001, 0), b"123456789001.0");
|
||||
deserialize_round_trip!(UDecimal::new(123456789001, 0), b"123456789001.0");
|
||||
deserialize_round_trip!(Decimal::new(-123456789001, 0), b"-123456789001.0");
|
||||
|
||||
deserialize_round_trip!(Decimal::new(123400000001, 8), b"1234.00000001");
|
||||
deserialize_round_trip!(UDecimal::new(123400000001, 8), b"1234.00000001");
|
||||
deserialize_round_trip!(Decimal::new(-123400000001, 8), b"-1234.00000001");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equality() {
|
||||
let d1 = Decimal::new(1234, 8);
|
||||
let d2 = Decimal::new(12340, 9);
|
||||
let d3 = Decimal::new(12340, 8);
|
||||
assert_eq!(d1, d1);
|
||||
assert_eq!(d1, d2);
|
||||
assert!(d1 != d3);
|
||||
assert!(d2 != d3);
|
||||
|
||||
assert!(d1 <= d1);
|
||||
assert!(d2 <= d2);
|
||||
assert!(d3 <= d3);
|
||||
assert!(d1 <= d2);
|
||||
assert!(d1 <= d3);
|
||||
assert!(d3 > d1);
|
||||
assert!(d3 > d2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arithmetic() {
|
||||
let d1 = Decimal::new(5, 1); // 0.5
|
||||
let d2 = Decimal::new(-2, 2); // -0.02
|
||||
let d3 = Decimal::new(3, 0); // 3.0
|
||||
let d4 = Decimal::new(0, 5); // 0.00000
|
||||
let u1 = UDecimal::new(5, 1); // 0.5
|
||||
let u3 = UDecimal::new(3, 0); // 3.0
|
||||
let u4 = UDecimal::new(0, 5); // 0.00000
|
||||
|
||||
assert!(d1.nonnegative());
|
||||
assert!(!d2.nonnegative());
|
||||
assert!(d3.nonnegative());
|
||||
assert!(d4.nonnegative());
|
||||
|
||||
assert_eq!(d1 + d2, Decimal::new(48, 2));
|
||||
assert_eq!(d1 - d2, Decimal::new(52, 2));
|
||||
assert_eq!(d1 + d3, Decimal::new(35, 1));
|
||||
assert_eq!(u1 + u3, UDecimal::new(35, 1));
|
||||
assert_eq!(d1 - d3, Decimal::new(-25, 1));
|
||||
assert_eq!(d2 + d3, Decimal::new(298, 2));
|
||||
assert_eq!(d2 - d3, Decimal::new(-302, 2));
|
||||
|
||||
assert_eq!(d1 + d4, d1);
|
||||
assert_eq!(u1 + u4, u1);
|
||||
assert_eq!(d1 - d4, d1);
|
||||
assert_eq!(d1 + d4, d1 - d4);
|
||||
assert_eq!(d4 + d4, d4);
|
||||
assert_eq!(u4 + u4, u4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
fn json_parse() {
|
||||
let json = Json::from_str("0.00980000").unwrap();
|
||||
assert_eq!(json.to_bytes(), b"0.00980000");
|
||||
let dec: Decimal = json.into_deserialize().unwrap();
|
||||
assert_eq!(dec, Decimal::new(980000, 8));
|
||||
|
||||
let json = Json::from_str("0.00980000").unwrap();
|
||||
assert_eq!(json.to_bytes(), b"0.00980000");
|
||||
let dec: UDecimal = json.into_deserialize().unwrap();
|
||||
assert_eq!(dec, UDecimal::new(980000, 8));
|
||||
|
||||
let json = Json::from_str("0.00980").unwrap();
|
||||
assert_eq!(json.to_bytes(), b"0.00980");
|
||||
let dec: Decimal = json.into_deserialize().unwrap();
|
||||
assert_eq!(dec, Decimal::new(98000, 7));
|
||||
|
||||
let json = Json::from_str("0.00980").unwrap();
|
||||
assert_eq!(json.to_bytes(), b"0.00980");
|
||||
let dec: UDecimal = json.into_deserialize().unwrap();
|
||||
assert_eq!(dec, UDecimal::new(98000, 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde-decimal")]
|
||||
fn parse_decimal_udecimal() {
|
||||
let dec = "0.00980000".parse::<Decimal>().unwrap();
|
||||
assert_eq!(dec, Decimal::new(980000, 8));
|
||||
|
||||
let dec = "0.00980000".parse::<UDecimal>().unwrap();
|
||||
assert_eq!(dec, UDecimal::new(980000, 8));
|
||||
|
||||
let dec = "0.00980".parse::<Decimal>().unwrap();
|
||||
assert_eq!(dec, Decimal::new(98000, 7));
|
||||
|
||||
let dec = "0.00980".parse::<UDecimal>().unwrap();
|
||||
assert_eq!(dec, UDecimal::new(98000, 7));
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ pub mod base58;
|
|||
pub mod bip32;
|
||||
pub mod bip143;
|
||||
pub mod contracthash;
|
||||
pub mod decimal;
|
||||
pub mod hash;
|
||||
pub mod misc;
|
||||
pub mod psbt;
|
||||
|
|
Loading…
Reference in New Issue