Skip to content

Commit

Permalink
Add helper for sorted maps, trim comments in validate.
Browse files Browse the repository at this point in the history
  • Loading branch information
graydon committed Jul 19, 2022
1 parent 7f53fc1 commit bce94ee
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 39 deletions.
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ pub use scval_conversions::*;
mod scval_validations;
#[cfg(feature = "next")]
pub use scval_validations::*;

#[cfg(feature = "next")]
mod scmap;
#[cfg(feature = "next")]
pub use scmap::*;
66 changes: 66 additions & 0 deletions src/scmap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use crate::{ScMap, ScMapEntry, ScVal, Validate};

impl ScMap {
pub fn sorted_from_entries<I: Iterator<Item = ScMapEntry>>(entries: I) -> Result<ScMap, ()> {
let mut v: Vec<ScMapEntry> = entries.collect();
v.sort_by(|a, b| a.key.cmp(&b.key));
let m = ScMap(v.try_into()?);
// `validate` here will further check that there are no duplicates.
m.validate()?;
Ok(m)
}

pub fn sorted_from_pairs<K, V, I: Iterator<Item = (K, V)>>(pairs: I) -> Result<ScMap, ()>
where
ScVal: From<K>,
ScVal: From<V>,
{
Self::sorted_from_entries(pairs.map(|(key, val)| ScMapEntry {
key: key.into(),
val: val.into(),
}))
}

pub fn sorted_from<K, V, II: IntoIterator<Item = (K, V)>>(src: II) -> Result<ScMap, ()>
where
ScVal: From<K>,
ScVal: From<V>,
{
Self::sorted_from_pairs(src.into_iter())
}
}

#[cfg(test)]
mod test {
use crate::ScMap;
use std::collections::BTreeMap;

#[test]
fn scmap_from_map() -> Result<(), ()> {
let mut m: BTreeMap<u32, u32> = BTreeMap::new();
m.insert(1, 2);
m.insert(5, 6);
m.insert(3, 4);
let scm = ScMap::sorted_from(m)?;
assert_eq!(scm.0.first().unwrap().key, 1u32.into());
assert_eq!(scm.0.last().unwrap().key, 5u32.into());
Ok(())
}

#[test]
fn scmap_from_pairs() -> Result<(), ()> {
let pairs: Vec<(u32, u32)> = vec![(3, 4), (5, 6), (1, 2)];
let scm = ScMap::sorted_from(pairs)?;
assert_eq!(scm.0.first().unwrap().key, 1u32.into());
assert_eq!(scm.0.last().unwrap().key, 5u32.into());
Ok(())
}

#[test]
fn scmap_from_bad_pairs() -> Result<(), ()> {
let pairs: Vec<(u32, u32)> = vec![(3, 4), (3, 5), (5, 6), (1, 2)];
let scm = ScMap::sorted_from(pairs);
assert!(scm.is_err());
Ok(())
}
}
42 changes: 3 additions & 39 deletions src/scval_validations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ impl Validate for ScMap {
type Error = ();

fn validate(&self) -> Result<(), Self::Error> {
// TODO: Is this sufficient? Is this sufficient for all future variants
// of ScVal? Should instead we serialize keys to XDR bytes and compare
// those?
// Check the map is sorted by key, and there are no keys that are
// duplicates.
if self.windows(2).all(|w| w[0].key < w[1].key) {
Expand All @@ -81,7 +78,7 @@ impl Validate for ScMap {

#[cfg(test)]
mod test {
use crate::{ScVal, Validate};
use crate::{ScMap, ScMapEntry, ScObject, ScVal, Validate};

#[test]
fn u63() {
Expand All @@ -108,9 +105,8 @@ mod test {

#[test]
fn map() {
// Values and objects in the data model have a total order. When
// comparing two values A and B:
// - If A is a positive int64 and B is not, A is less than B.
// Maps should be sorted by key and have no duplicates. The sort order
// is just the "normal" sort order on ScVal emitted by derive(PartialOrd).
assert_eq!(
ScVal::Object(Some(ScObject::Map(ScMap(
vec![
Expand All @@ -129,38 +125,6 @@ mod test {
.validate(),
Ok(())
);
// - If A and B are both positive int64 values, they are ordered by the
// normal int64 order.
// - If A and B are both tagged and if A has a lesser tag than B, A is
// less than B.
// - If A and B are both equally tagged, then:
// - If they have tag 0, they are ordered by the normal uint32 order
// on their low 32 bits.
// - If they have tag 1, they are ordered by the normal int32 order
// on their low 32 bits.
// - If they have tag 2, 5 or 6 or 7 they are ordered by the normal
// uint64 order on the zero-extension of their low 60 bits.
// - If they have tag 4 they are ordered by the lexicographical order
// of their Unicode string value.
// - If they have tag 3 they are ordered by calling obj_cmp(A, B)
// which performs deep object comparison.
//
// Deep object comparison can be accessed by either guest or host: it is
// provided to guests as a host function via the host environment
// interface. It performs a recursive structural comparison of objects
// and values embedded in objects using the following rules:
// - If A and B have different object types, they are ordered by object
// type code.
// - If A and B are boxes, their values are ordered by the value rules
// above.
// - If A and B are vectors, they are ordered by lexicographic
// extension of the value order
// - If A and B are maps, they are ordered lexicographically as ordered
// vectors of (key, value) pairs
// - If A and B are int64 or uint64, they are ordered using the normal
// order for those types
// - If A and B are binary, they are ordered using the lexicograhical
// order of their respective bytes
assert_eq!(
ScVal::Object(Some(ScObject::Map(ScMap(
vec![
Expand Down

0 comments on commit bce94ee

Please sign in to comment.