Skip to content

Commit 6369c71

Browse files
authored
Merge pull request #59 from b41sh/fix-object-compare
fix: fix compare object value with different length panic
2 parents db4cd7a + d7f9cb8 commit 6369c71

File tree

6 files changed

+57
-42
lines changed

6 files changed

+57
-42
lines changed

src/de.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl<'a> Decoder<'a> {
117117
}
118118
NUMBER_TAG => {
119119
let offset = jentry.length as usize;
120-
let n = Number::decode(&self.buf[..offset]);
120+
let n = Number::decode(&self.buf[..offset])?;
121121
self.buf = &self.buf[offset..];
122122
Ok(Value::Number(n))
123123
}

src/error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub enum Error {
7979
InvalidJsonb,
8080
InvalidJsonbHeader,
8181
InvalidJsonbJEntry,
82+
InvalidJsonbNumber,
8283

8384
InvalidJsonPath,
8485
InvalidJsonPathPredicate,

src/functions.rs

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,9 +1003,9 @@ fn compare_scalar(
10031003
}
10041004
(NUMBER_TAG, NUMBER_TAG) => {
10051005
let left_offset = left_jentry.length as usize;
1006-
let left_num = Number::decode(&left[..left_offset]);
1006+
let left_num = Number::decode(&left[..left_offset])?;
10071007
let right_offset = right_jentry.length as usize;
1008-
let right_num = Number::decode(&right[..right_offset]);
1008+
let right_num = Number::decode(&right[..right_offset])?;
10091009
Ok(left_num.cmp(&right_num))
10101010
}
10111011
(TRUE_TAG, TRUE_TAG) => Ok(Ordering::Equal),
@@ -1047,7 +1047,7 @@ fn compare_array(
10471047
let mut jentry_offset = 0;
10481048
let mut left_val_offset = 4 * left_length;
10491049
let mut right_val_offset = 4 * right_length;
1050-
let length = if left_length < right_length {
1050+
let length = if left_length <= right_length {
10511051
left_length
10521052
} else {
10531053
right_length
@@ -1088,34 +1088,39 @@ fn compare_object(
10881088
let left_length = (left_header & CONTAINER_HEADER_LEN_MASK) as usize;
10891089
let right_length = (right_header & CONTAINER_HEADER_LEN_MASK) as usize;
10901090

1091-
let mut jentry_offset = 0;
1091+
let mut left_jentry_offset = 0;
10921092
let mut left_val_offset = 8 * left_length;
1093+
let mut right_jentry_offset = 0;
10931094
let mut right_val_offset = 8 * right_length;
10941095

1095-
let length = if left_length < right_length {
1096-
left_length
1097-
} else {
1098-
right_length
1099-
};
1100-
// read all key jentries first
1101-
let mut left_key_jentries: VecDeque<JEntry> = VecDeque::with_capacity(length);
1102-
let mut right_key_jentries: VecDeque<JEntry> = VecDeque::with_capacity(length);
1103-
for _ in 0..length {
1104-
let left_encoded = read_u32(left, jentry_offset)?;
1096+
// read all left key jentries and right key jentries first.
1097+
// Note: since the values are stored after the keys,
1098+
// we must first read all the key jentries to get the correct value offset.
1099+
let mut left_key_jentries: VecDeque<JEntry> = VecDeque::with_capacity(left_length);
1100+
let mut right_key_jentries: VecDeque<JEntry> = VecDeque::with_capacity(right_length);
1101+
for _ in 0..left_length {
1102+
let left_encoded = read_u32(left, left_jentry_offset)?;
11051103
let left_key_jentry = JEntry::decode_jentry(left_encoded);
1106-
let right_encoded = read_u32(right, jentry_offset)?;
1107-
let right_key_jentry = JEntry::decode_jentry(right_encoded);
11081104

1109-
jentry_offset += 4;
1105+
left_jentry_offset += 4;
11101106
left_val_offset += left_key_jentry.length as usize;
1111-
right_val_offset += right_key_jentry.length as usize;
1112-
11131107
left_key_jentries.push_back(left_key_jentry);
1108+
}
1109+
for _ in 0..right_length {
1110+
let right_encoded = read_u32(right, right_jentry_offset)?;
1111+
let right_key_jentry = JEntry::decode_jentry(right_encoded);
1112+
1113+
right_jentry_offset += 4;
1114+
right_val_offset += right_key_jentry.length as usize;
11141115
right_key_jentries.push_back(right_key_jentry);
11151116
}
11161117

1117-
let mut left_jentry_offset = 4 * left_length;
1118-
let mut right_jentry_offset = 4 * right_length;
1118+
let length = if left_length <= right_length {
1119+
left_length
1120+
} else {
1121+
right_length
1122+
};
1123+
11191124
let mut left_key_offset = 8 * left_length;
11201125
let mut right_key_offset = 8 * right_length;
11211126
for _ in 0..length {
@@ -1247,8 +1252,7 @@ pub fn as_number(value: &[u8]) -> Option<Number> {
12471252
match jentry.type_code {
12481253
NUMBER_TAG => {
12491254
let length = jentry.length as usize;
1250-
let num = Number::decode(&value[8..8 + length]);
1251-
Some(num)
1255+
Number::decode(&value[8..8 + length]).ok()
12521256
}
12531257
_ => None,
12541258
}
@@ -1523,7 +1527,7 @@ fn scalar_to_serde_json(jentry: JEntry, value: &[u8]) -> Result<serde_json::Valu
15231527
FALSE_TAG => serde_json::Value::Bool(false),
15241528
NUMBER_TAG => {
15251529
let len = jentry.length as usize;
1526-
let n = Number::decode(&value[..len]);
1530+
let n = Number::decode(&value[..len])?;
15271531
match n {
15281532
Number::Int64(v) => serde_json::Value::Number(serde_json::Number::from(v)),
15291533
Number::UInt64(v) => serde_json::Value::Number(serde_json::Number::from(v)),
@@ -1733,7 +1737,7 @@ fn scalar_to_string(
17331737
TRUE_TAG => json.push_str("true"),
17341738
FALSE_TAG => json.push_str("false"),
17351739
NUMBER_TAG => {
1736-
let num = Number::decode(&value[*value_offset..*value_offset + length]);
1740+
let num = Number::decode(&value[*value_offset..*value_offset + length])?;
17371741
json.push_str(&num.to_string());
17381742
}
17391743
STRING_TAG => {
@@ -1860,15 +1864,16 @@ fn scalar_convert_to_comparable(depth: u8, jentry: &JEntry, value: &[u8], buf: &
18601864
}
18611865
NUMBER_TAG => {
18621866
let length = jentry.length as usize;
1863-
let num = Number::decode(&value[..length]);
1864-
let n = num.as_f64().unwrap();
1865-
// https://github.com/rust-lang/rust/blob/9c20b2a8cc7588decb6de25ac6a7912dcef24d65/library/core/src/num/f32.rs#L1176-L1260
1866-
let s = n.to_bits() as i64;
1867-
let v = s ^ (((s >> 63) as u64) >> 1) as i64;
1868-
let mut b = v.to_be_bytes();
1869-
// Toggle top "sign" bit to ensure consistent sort order
1870-
b[0] ^= 0x80;
1871-
buf.extend_from_slice(&b);
1867+
if let Ok(num) = Number::decode(&value[..length]) {
1868+
let n = num.as_f64().unwrap();
1869+
// https://github.com/rust-lang/rust/blob/9c20b2a8cc7588decb6de25ac6a7912dcef24d65/library/core/src/num/f32.rs#L1176-L1260
1870+
let s = n.to_bits() as i64;
1871+
let v = s ^ (((s >> 63) as u64) >> 1) as i64;
1872+
let mut b = v.to_be_bytes();
1873+
// Toggle top "sign" bit to ensure consistent sort order
1874+
b[0] ^= 0x80;
1875+
buf.extend_from_slice(&b);
1876+
}
18721877
}
18731878
_ => {}
18741879
}

src/jsonpath/selector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ impl<'a> Selector<'a> {
556556
TRUE_TAG => PathValue::Boolean(true),
557557
FALSE_TAG => PathValue::Boolean(false),
558558
NUMBER_TAG => {
559-
let n = Number::decode(&root[offset..offset + length]);
559+
let n = Number::decode(&root[offset..offset + length])?;
560560
PathValue::Number(n)
561561
}
562562
STRING_TAG => {

src/number.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,13 @@ impl Number {
9494
}
9595

9696
#[inline]
97-
pub fn decode(bytes: &[u8]) -> Number {
97+
pub fn decode(bytes: &[u8]) -> Result<Number, Error> {
9898
let mut len = bytes.len();
9999
assert!(len > 0);
100100
len -= 1;
101101

102102
let ty = bytes[0];
103-
match ty {
103+
let num = match ty {
104104
NUMBER_ZERO => Number::UInt64(0),
105105
NUMBER_NAN => Number::Float64(f64::NAN),
106106
NUMBER_INF => Number::Float64(f64::INFINITY),
@@ -110,18 +110,25 @@ impl Number {
110110
2 => Number::Int64(i16::from_be_bytes(bytes[1..].try_into().unwrap()) as i64),
111111
4 => Number::Int64(i32::from_be_bytes(bytes[1..].try_into().unwrap()) as i64),
112112
8 => Number::Int64(i64::from_be_bytes(bytes[1..].try_into().unwrap())),
113-
_ => unreachable!(),
113+
_ => {
114+
return Err(Error::InvalidJsonbNumber);
115+
}
114116
},
115117
NUMBER_UINT => match len {
116118
1 => Number::UInt64(u8::from_be_bytes(bytes[1..].try_into().unwrap()) as u64),
117119
2 => Number::UInt64(u16::from_be_bytes(bytes[1..].try_into().unwrap()) as u64),
118120
4 => Number::UInt64(u32::from_be_bytes(bytes[1..].try_into().unwrap()) as u64),
119121
8 => Number::UInt64(u64::from_be_bytes(bytes[1..].try_into().unwrap())),
120-
_ => unreachable!(),
122+
_ => {
123+
return Err(Error::InvalidJsonbNumber);
124+
}
121125
},
122126
NUMBER_FLOAT => Number::Float64(f64::from_be_bytes(bytes[1..].try_into().unwrap())),
123-
_ => unreachable!(),
124-
}
127+
_ => {
128+
return Err(Error::InvalidJsonbNumber);
129+
}
130+
};
131+
Ok(num)
125132
}
126133

127134
pub fn as_i64(&self) -> Option<i64> {

tests/it/functions.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,8 @@ fn test_compare() {
584584
r#"{"k1":"a1","k2":"v2"}"#,
585585
Ordering::Greater,
586586
),
587+
(r#"{"k1":123}"#, r#"{"k1":123,"k2":111}"#, Ordering::Less),
588+
(r#"{"k1":123,"k2":111}"#, r#"{"k1":123}"#, Ordering::Greater),
587589
(r#"{"k1":"v1","k2":"v2"}"#, r#"{"a":1}"#, Ordering::Greater),
588590
(r#"{"k1":"v1","k2":"v2"}"#, r#"{}"#, Ordering::Greater),
589591
(r#"{"k1":"v1","k2":"v2"}"#, r#""ab""#, Ordering::Greater),

0 commit comments

Comments
 (0)