Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix compare object value with different length panic #59

Merged
merged 1 commit into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl<'a> Decoder<'a> {
}
NUMBER_TAG => {
let offset = jentry.length as usize;
let n = Number::decode(&self.buf[..offset]);
let n = Number::decode(&self.buf[..offset])?;
self.buf = &self.buf[offset..];
Ok(Value::Number(n))
}
Expand Down
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub enum Error {
InvalidJsonb,
InvalidJsonbHeader,
InvalidJsonbJEntry,
InvalidJsonbNumber,

InvalidJsonPath,
InvalidJsonPathPredicate,
Expand Down
73 changes: 39 additions & 34 deletions src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1003,9 +1003,9 @@ fn compare_scalar(
}
(NUMBER_TAG, NUMBER_TAG) => {
let left_offset = left_jentry.length as usize;
let left_num = Number::decode(&left[..left_offset]);
let left_num = Number::decode(&left[..left_offset])?;
let right_offset = right_jentry.length as usize;
let right_num = Number::decode(&right[..right_offset]);
let right_num = Number::decode(&right[..right_offset])?;
Ok(left_num.cmp(&right_num))
}
(TRUE_TAG, TRUE_TAG) => Ok(Ordering::Equal),
Expand Down Expand Up @@ -1047,7 +1047,7 @@ fn compare_array(
let mut jentry_offset = 0;
let mut left_val_offset = 4 * left_length;
let mut right_val_offset = 4 * right_length;
let length = if left_length < right_length {
let length = if left_length <= right_length {
left_length
} else {
right_length
Expand Down Expand Up @@ -1088,34 +1088,39 @@ fn compare_object(
let left_length = (left_header & CONTAINER_HEADER_LEN_MASK) as usize;
let right_length = (right_header & CONTAINER_HEADER_LEN_MASK) as usize;

let mut jentry_offset = 0;
let mut left_jentry_offset = 0;
let mut left_val_offset = 8 * left_length;
let mut right_jentry_offset = 0;
let mut right_val_offset = 8 * right_length;

let length = if left_length < right_length {
left_length
} else {
right_length
};
// read all key jentries first
let mut left_key_jentries: VecDeque<JEntry> = VecDeque::with_capacity(length);
let mut right_key_jentries: VecDeque<JEntry> = VecDeque::with_capacity(length);
for _ in 0..length {
let left_encoded = read_u32(left, jentry_offset)?;
// read all left key jentries and right key jentries first.
// Note: since the values are stored after the keys,
// we must first read all the key jentries to get the correct value offset.
let mut left_key_jentries: VecDeque<JEntry> = VecDeque::with_capacity(left_length);
let mut right_key_jentries: VecDeque<JEntry> = VecDeque::with_capacity(right_length);
for _ in 0..left_length {
let left_encoded = read_u32(left, left_jentry_offset)?;
let left_key_jentry = JEntry::decode_jentry(left_encoded);
let right_encoded = read_u32(right, jentry_offset)?;
let right_key_jentry = JEntry::decode_jentry(right_encoded);

jentry_offset += 4;
left_jentry_offset += 4;
left_val_offset += left_key_jentry.length as usize;
right_val_offset += right_key_jentry.length as usize;

left_key_jentries.push_back(left_key_jentry);
}
for _ in 0..right_length {
let right_encoded = read_u32(right, right_jentry_offset)?;
let right_key_jentry = JEntry::decode_jentry(right_encoded);

right_jentry_offset += 4;
right_val_offset += right_key_jentry.length as usize;
right_key_jentries.push_back(right_key_jentry);
}

let mut left_jentry_offset = 4 * left_length;
let mut right_jentry_offset = 4 * right_length;
let length = if left_length <= right_length {
left_length
} else {
right_length
};

let mut left_key_offset = 8 * left_length;
let mut right_key_offset = 8 * right_length;
for _ in 0..length {
Expand Down Expand Up @@ -1247,8 +1252,7 @@ pub fn as_number(value: &[u8]) -> Option<Number> {
match jentry.type_code {
NUMBER_TAG => {
let length = jentry.length as usize;
let num = Number::decode(&value[8..8 + length]);
Some(num)
Number::decode(&value[8..8 + length]).ok()
}
_ => None,
}
Expand Down Expand Up @@ -1523,7 +1527,7 @@ fn scalar_to_serde_json(jentry: JEntry, value: &[u8]) -> Result<serde_json::Valu
FALSE_TAG => serde_json::Value::Bool(false),
NUMBER_TAG => {
let len = jentry.length as usize;
let n = Number::decode(&value[..len]);
let n = Number::decode(&value[..len])?;
match n {
Number::Int64(v) => serde_json::Value::Number(serde_json::Number::from(v)),
Number::UInt64(v) => serde_json::Value::Number(serde_json::Number::from(v)),
Expand Down Expand Up @@ -1733,7 +1737,7 @@ fn scalar_to_string(
TRUE_TAG => json.push_str("true"),
FALSE_TAG => json.push_str("false"),
NUMBER_TAG => {
let num = Number::decode(&value[*value_offset..*value_offset + length]);
let num = Number::decode(&value[*value_offset..*value_offset + length])?;
json.push_str(&num.to_string());
}
STRING_TAG => {
Expand Down Expand Up @@ -1860,15 +1864,16 @@ fn scalar_convert_to_comparable(depth: u8, jentry: &JEntry, value: &[u8], buf: &
}
NUMBER_TAG => {
let length = jentry.length as usize;
let num = Number::decode(&value[..length]);
let n = num.as_f64().unwrap();
// https://github.com/rust-lang/rust/blob/9c20b2a8cc7588decb6de25ac6a7912dcef24d65/library/core/src/num/f32.rs#L1176-L1260
let s = n.to_bits() as i64;
let v = s ^ (((s >> 63) as u64) >> 1) as i64;
let mut b = v.to_be_bytes();
// Toggle top "sign" bit to ensure consistent sort order
b[0] ^= 0x80;
buf.extend_from_slice(&b);
if let Ok(num) = Number::decode(&value[..length]) {
let n = num.as_f64().unwrap();
// https://github.com/rust-lang/rust/blob/9c20b2a8cc7588decb6de25ac6a7912dcef24d65/library/core/src/num/f32.rs#L1176-L1260
let s = n.to_bits() as i64;
let v = s ^ (((s >> 63) as u64) >> 1) as i64;
let mut b = v.to_be_bytes();
// Toggle top "sign" bit to ensure consistent sort order
b[0] ^= 0x80;
buf.extend_from_slice(&b);
}
}
_ => {}
}
Expand Down
2 changes: 1 addition & 1 deletion src/jsonpath/selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ impl<'a> Selector<'a> {
TRUE_TAG => PathValue::Boolean(true),
FALSE_TAG => PathValue::Boolean(false),
NUMBER_TAG => {
let n = Number::decode(&root[offset..offset + length]);
let n = Number::decode(&root[offset..offset + length])?;
PathValue::Number(n)
}
STRING_TAG => {
Expand Down
19 changes: 13 additions & 6 deletions src/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ impl Number {
}

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

let ty = bytes[0];
match ty {
let num = match ty {
NUMBER_ZERO => Number::UInt64(0),
NUMBER_NAN => Number::Float64(f64::NAN),
NUMBER_INF => Number::Float64(f64::INFINITY),
Expand All @@ -110,18 +110,25 @@ impl Number {
2 => Number::Int64(i16::from_be_bytes(bytes[1..].try_into().unwrap()) as i64),
4 => Number::Int64(i32::from_be_bytes(bytes[1..].try_into().unwrap()) as i64),
8 => Number::Int64(i64::from_be_bytes(bytes[1..].try_into().unwrap())),
_ => unreachable!(),
_ => {
return Err(Error::InvalidJsonbNumber);
}
},
NUMBER_UINT => match len {
1 => Number::UInt64(u8::from_be_bytes(bytes[1..].try_into().unwrap()) as u64),
2 => Number::UInt64(u16::from_be_bytes(bytes[1..].try_into().unwrap()) as u64),
4 => Number::UInt64(u32::from_be_bytes(bytes[1..].try_into().unwrap()) as u64),
8 => Number::UInt64(u64::from_be_bytes(bytes[1..].try_into().unwrap())),
_ => unreachable!(),
_ => {
return Err(Error::InvalidJsonbNumber);
}
},
NUMBER_FLOAT => Number::Float64(f64::from_be_bytes(bytes[1..].try_into().unwrap())),
_ => unreachable!(),
}
_ => {
return Err(Error::InvalidJsonbNumber);
}
};
Ok(num)
}

pub fn as_i64(&self) -> Option<i64> {
Expand Down
2 changes: 2 additions & 0 deletions tests/it/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,8 @@ fn test_compare() {
r#"{"k1":"a1","k2":"v2"}"#,
Ordering::Greater,
),
(r#"{"k1":123}"#, r#"{"k1":123,"k2":111}"#, Ordering::Less),
(r#"{"k1":123,"k2":111}"#, r#"{"k1":123}"#, Ordering::Greater),
(r#"{"k1":"v1","k2":"v2"}"#, r#"{"a":1}"#, Ordering::Greater),
(r#"{"k1":"v1","k2":"v2"}"#, r#"{}"#, Ordering::Greater),
(r#"{"k1":"v1","k2":"v2"}"#, r#""ab""#, Ordering::Greater),
Expand Down
Loading