121 lines
3.7 KiB
Rust
121 lines
3.7 KiB
Rust
//! PostgreSQL 文档规定的 NUMERIC 分组字节;预期值独立于编码实现。
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn numeric_encoding_matches_protocol_words() {
|
|
for (text, precision, scale, words) in [
|
|
("0", 5, 2, vec![0, 0, 0, 2]),
|
|
("-12.34", 5, 2, vec![2, 0, 0x4000, 2, 12, 3400]),
|
|
("0.00001", 5, 5, vec![1, -2, 0, 5, 1000]),
|
|
("10000", 5, 0, vec![1, 1, 0, 0, 1]),
|
|
("12345678.9012", 12, 4, vec![3, 1, 0, 4, 1234, 5678, 9012]),
|
|
(
|
|
"9999999999999999999999999999999999",
|
|
34,
|
|
0,
|
|
vec![
|
|
9, 8, 0, 0, 99, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
|
|
],
|
|
),
|
|
] {
|
|
let value = Decimal::parse(text).unwrap();
|
|
let expected = words
|
|
.into_iter()
|
|
.flat_map(i16::to_be_bytes)
|
|
.collect::<Vec<_>>();
|
|
let mut output = BytesMut::new();
|
|
encode(value, precision, scale, &mut output).unwrap();
|
|
assert_eq!(output.as_ref(), expected, "{text}");
|
|
assert_eq!(decode(&output).unwrap(), value);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn native_numeric_preserves_decimal_extremes_and_zero() {
|
|
for text in [
|
|
"0",
|
|
"-0",
|
|
"0E-6176",
|
|
"1E-6176",
|
|
"1E6111",
|
|
"-123.456",
|
|
"1.0000",
|
|
"9.999999999999999999999999999999999E6144",
|
|
] {
|
|
let value = Decimal::parse(text).unwrap();
|
|
let mut output = BytesMut::new();
|
|
encode_native(value, &mut output);
|
|
assert_eq!(decode(&output).unwrap(), value, "{text}");
|
|
}
|
|
for (text, precision, scale) in [("1.001", 5, 2), ("1000", 5, 2), ("1", 35, 0), ("1", 2, 3)] {
|
|
assert!(
|
|
encode(
|
|
Decimal::parse(text).unwrap(),
|
|
precision,
|
|
scale,
|
|
&mut BytesMut::new()
|
|
)
|
|
.is_err()
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn numeric_decode_rejects_malformed_or_inexact_values() {
|
|
for words in [
|
|
vec![1, 0, 0, 0],
|
|
vec![-1, 0, 0, 0],
|
|
vec![1, 0, 0, 0, 10000],
|
|
vec![0, 0, 0xc000_u16 as i16, 0],
|
|
vec![0, 0, 0, -1],
|
|
vec![1, 1537, 0, 0, 1],
|
|
vec![1, -1545, 0, 0, 1],
|
|
vec![
|
|
9, 8, 0, 0, 999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
|
|
],
|
|
] {
|
|
let bytes = words
|
|
.into_iter()
|
|
.flat_map(i16::to_be_bytes)
|
|
.collect::<Vec<_>>();
|
|
assert!(decode(&bytes).is_err(), "{bytes:?}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[ignore = "explicit release performance probe"]
|
|
fn numeric_decode_cost() {
|
|
use std::{hint::black_box, time::Instant};
|
|
println!("\ncase,sample,iterations,elapsed_ns,checksum");
|
|
for (name, text) in [
|
|
("zero", "0"),
|
|
("fraction", "-1234567890123456789012345.123456789"),
|
|
("exponent", "9.999999999999999999999999999999999E6144"),
|
|
] {
|
|
let value = Decimal::parse(text).unwrap();
|
|
let mut encoded = BytesMut::new();
|
|
encode_native(value, &mut encoded);
|
|
let expected = decode(&encoded).unwrap();
|
|
assert_eq!(expected, value);
|
|
for _ in 0..100 {
|
|
black_box(decode(&encoded).unwrap());
|
|
}
|
|
for sample in 0..15 {
|
|
let started = Instant::now();
|
|
let mut decoded = Decimal::ZERO;
|
|
for _ in 0..100_000 {
|
|
decoded = black_box(decode(black_box(&encoded)).unwrap());
|
|
}
|
|
let elapsed = started.elapsed().as_nanos();
|
|
assert_eq!(decoded, expected);
|
|
let checksum: u64 = decoded
|
|
.to_be_bytes()
|
|
.iter()
|
|
.map(|byte| u64::from(*byte))
|
|
.sum();
|
|
println!("numeric_decode_{name},{sample},100000,{elapsed},{checksum}");
|
|
}
|
|
}
|
|
}
|