-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1727 from AleoHQ/feat/array-type
[Feature] Introduce `ArrayType`.
- Loading branch information
Showing
8 changed files
with
363 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Copyright (C) 2019-2023 Aleo Systems Inc. | ||
// This file is part of the snarkVM library. | ||
|
||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at: | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use super::*; | ||
|
||
impl<N: Network> FromBytes for ArrayType<N> { | ||
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> { | ||
let variant = u8::read_le(&mut reader)?; | ||
match variant { | ||
0 => Ok(Self::new_literal(LiteralType::read_le(&mut reader)?, U32::read_le(&mut reader)?).map_err(error)?), | ||
1 => Ok(Self::new_struct(Identifier::read_le(&mut reader)?, U32::read_le(&mut reader)?).map_err(error)?), | ||
2.. => Err(error(format!("Failed to deserialize annotation variant {variant}"))), | ||
} | ||
} | ||
} | ||
|
||
impl<N: Network> ToBytes for ArrayType<N> { | ||
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> { | ||
match self { | ||
Self::Literal(literal_type, length) => { | ||
0u8.write_le(&mut writer)?; | ||
literal_type.write_le(&mut writer)?; | ||
length.write_le(&mut writer) | ||
} | ||
Self::Struct(identifier, length) => { | ||
1u8.write_le(&mut writer)?; | ||
identifier.write_le(&mut writer)?; | ||
length.write_le(&mut writer) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// Copyright (C) 2019-2023 Aleo Systems Inc. | ||
// This file is part of the snarkVM library. | ||
|
||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at: | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
mod bytes; | ||
mod parse; | ||
mod serialize; | ||
|
||
use crate::{Identifier, LiteralType, PlaintextType, U32}; | ||
use snarkvm_console_network::prelude::*; | ||
|
||
use core::fmt::{Debug, Display}; | ||
|
||
/// An `ArrayType` defines the type and size of an array. | ||
#[derive(Clone, PartialEq, Eq, Hash)] | ||
pub enum ArrayType<N: Network> { | ||
/// An array of literals. | ||
Literal(LiteralType, U32<N>), | ||
/// An array of structs. | ||
Struct(Identifier<N>, U32<N>), | ||
} | ||
|
||
impl<N: Network> ArrayType<N> { | ||
/// Initializes a new array type composed of literals. | ||
pub fn new_literal(literal_type: LiteralType, length: U32<N>) -> Result<Self> { | ||
ensure!(*length as usize >= N::MIN_ARRAY_ENTRIES, "An array must have {} element", N::MIN_ARRAY_ENTRIES); | ||
ensure!(*length as usize <= N::MAX_ARRAY_ENTRIES, "An array can contain {} elements", N::MAX_ARRAY_ENTRIES); | ||
Ok(Self::Literal(literal_type, length)) | ||
} | ||
|
||
/// Initializes a new array type composed of structs. | ||
pub fn new_struct(struct_: Identifier<N>, length: U32<N>) -> Result<Self> { | ||
ensure!(*length as usize >= N::MIN_ARRAY_ENTRIES, "An array must have {} element", N::MIN_ARRAY_ENTRIES); | ||
ensure!(*length as usize <= N::MAX_ARRAY_ENTRIES, "An array can contain {} elements", N::MAX_ARRAY_ENTRIES); | ||
Ok(Self::Struct(struct_, length)) | ||
} | ||
} | ||
|
||
impl<N: Network> ArrayType<N> { | ||
/// Returns the element type. | ||
pub const fn element_type(&self) -> PlaintextType<N> { | ||
match &self { | ||
ArrayType::Literal(literal_type, ..) => PlaintextType::Literal(*literal_type), | ||
ArrayType::Struct(identifier, ..) => PlaintextType::Struct(*identifier), | ||
} | ||
} | ||
|
||
/// Returns `true` if the array is empty. | ||
pub fn is_empty(&self) -> bool { | ||
match &self { | ||
ArrayType::Literal(_, length) => **length == 0, | ||
ArrayType::Struct(_, length) => **length == 0, | ||
} | ||
} | ||
|
||
/// Returns the length of the array. | ||
pub const fn length(&self) -> &U32<N> { | ||
match &self { | ||
ArrayType::Literal(_, length) => length, | ||
ArrayType::Struct(_, length) => length, | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use snarkvm_console_network::Testnet3; | ||
|
||
use core::str::FromStr; | ||
|
||
type CurrentNetwork = Testnet3; | ||
|
||
#[test] | ||
fn test_array_type() -> Result<()> { | ||
// Test literal array types. | ||
let array = ArrayType::<CurrentNetwork>::from_str("[field; 4]")?; | ||
assert_eq!(array, ArrayType::<CurrentNetwork>::Literal(LiteralType::Field, U32::new(4))); | ||
assert_eq!( | ||
array.to_bytes_le()?, | ||
ArrayType::<CurrentNetwork>::from_bytes_le(&array.to_bytes_le()?)?.to_bytes_le()? | ||
); | ||
assert_eq!(array.element_type(), PlaintextType::Literal(LiteralType::Field)); | ||
assert_eq!(array.length(), &U32::new(4)); | ||
assert!(!array.is_empty()); | ||
|
||
// Test struct array types. | ||
let array = ArrayType::<CurrentNetwork>::from_str("[foo; 1]")?; | ||
assert_eq!(array, ArrayType::<CurrentNetwork>::Struct(Identifier::from_str("foo")?, U32::new(1))); | ||
assert_eq!( | ||
array.to_bytes_le()?, | ||
ArrayType::<CurrentNetwork>::from_bytes_le(&array.to_bytes_le()?)?.to_bytes_le()? | ||
); | ||
assert_eq!(array.element_type(), PlaintextType::Struct(Identifier::from_str("foo")?)); | ||
assert_eq!(array.length(), &U32::new(1)); | ||
assert!(!array.is_empty()); | ||
|
||
// Test array type with maximum length. | ||
let array = ArrayType::<CurrentNetwork>::from_str("[scalar; 32]")?; | ||
assert_eq!(array, ArrayType::<CurrentNetwork>::Literal(LiteralType::Scalar, U32::new(32))); | ||
assert_eq!( | ||
array.to_bytes_le()?, | ||
ArrayType::<CurrentNetwork>::from_bytes_le(&array.to_bytes_le()?)?.to_bytes_le()? | ||
); | ||
assert_eq!(array.element_type(), PlaintextType::Literal(LiteralType::Scalar)); | ||
assert_eq!(array.length(), &U32::new(32)); | ||
assert!(!array.is_empty()); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_array_type_fails() { | ||
let type_ = ArrayType::<CurrentNetwork>::from_str("[field; 0]"); | ||
assert!(type_.is_err()); | ||
|
||
let type_ = ArrayType::<CurrentNetwork>::from_str("[field; 4294967296]"); | ||
assert!(type_.is_err()); | ||
|
||
let type_ = ArrayType::<CurrentNetwork>::from_str("[foo; -1]"); | ||
assert!(type_.is_err()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright (C) 2019-2023 Aleo Systems Inc. | ||
// This file is part of the snarkVM library. | ||
|
||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at: | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use super::*; | ||
|
||
impl<N: Network> Parser for ArrayType<N> { | ||
/// Parses a string into a literal type. | ||
#[inline] | ||
fn parse(string: &str) -> ParserResult<Self> { | ||
// Parse the opening bracket. | ||
let (string, _) = tag("[")(string)?; | ||
// Parse the whitespaces from the string. | ||
let (string, _) = Sanitizer::parse_whitespaces(string)?; | ||
|
||
// A helper function to parse the semicolon, length, and closing bracket. | ||
fn parse_length(string: &str) -> ParserResult<u32> { | ||
// Parse the whitespaces from the string. | ||
let (string, _) = Sanitizer::parse_whitespaces(string)?; | ||
// Parse the semicolon. | ||
let (string, _) = tag(";")(string)?; | ||
// Parse the whitespaces from the string. | ||
let (string, _) = Sanitizer::parse_whitespaces(string)?; | ||
// Parse the length from the string. | ||
let (string, length) = | ||
map_res(recognize(many1(one_of("0123456789"))), |digits: &str| digits.parse::<u32>())(string)?; | ||
// Parse the whitespaces from the string. | ||
let (string, _) = Sanitizer::parse_whitespaces(string)?; | ||
// Parse the closing bracket. | ||
let (string, _) = tag("]")(string)?; | ||
Ok((string, length)) | ||
} | ||
|
||
// Parse the element type, followed by the length. | ||
alt(( | ||
map_res(pair(LiteralType::parse, parse_length), |(element_type, length)| { | ||
ArrayType::new_literal(element_type, U32::new(length)) | ||
}), | ||
map_res(pair(Identifier::parse, parse_length), |(element_type, length)| { | ||
ArrayType::new_struct(element_type, U32::new(length)) | ||
}), | ||
))(string) | ||
} | ||
} | ||
|
||
impl<N: Network> FromStr for ArrayType<N> { | ||
type Err = Error; | ||
|
||
/// Returns an array type from a string literal. | ||
fn from_str(string: &str) -> Result<Self> { | ||
match Self::parse(string) { | ||
Ok((remainder, object)) => { | ||
// Ensure the remainder is empty. | ||
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\""); | ||
// Return the object. | ||
Ok(object) | ||
} | ||
Err(error) => bail!("Failed to parse string. {error}"), | ||
} | ||
} | ||
} | ||
|
||
impl<N: Network> Debug for ArrayType<N> { | ||
/// Prints the array type as a string. | ||
fn fmt(&self, f: &mut Formatter) -> fmt::Result { | ||
Display::fmt(self, f) | ||
} | ||
} | ||
|
||
impl<N: Network> Display for ArrayType<N> { | ||
/// Prints the array type as a string. | ||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::Literal(literal_type, length) => write!(f, "[{}; {}]", literal_type, **length), | ||
Self::Struct(identifier, length) => write!(f, "[{}; {}]", identifier, **length), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright (C) 2019-2023 Aleo Systems Inc. | ||
// This file is part of the snarkVM library. | ||
|
||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at: | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use super::*; | ||
|
||
impl<N: Network> Serialize for ArrayType<N> { | ||
/// Serializes the array type into string or bytes. | ||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { | ||
match serializer.is_human_readable() { | ||
true => serializer.collect_str(self), | ||
false => ToBytesSerializer::serialize_with_size_encoding(self, serializer), | ||
} | ||
} | ||
} | ||
|
||
impl<'de, N: Network> Deserialize<'de> for ArrayType<N> { | ||
/// Deserializes the array type from a string or bytes. | ||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { | ||
match deserializer.is_human_readable() { | ||
true => FromStr::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom), | ||
false => FromBytesDeserializer::<Self>::deserialize_with_size_encoding(deserializer, "array type"), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use snarkvm_console_network::Testnet3; | ||
|
||
/// Add test cases here to be checked for serialization. | ||
const TEST_CASES: &[&str] = &["[u8; 1]", "[foo; 4]", "[boolean; 31]", "[field; 32]"]; | ||
|
||
fn check_serde_json< | ||
T: Serialize + for<'a> Deserialize<'a> + Debug + Display + PartialEq + Eq + FromStr + ToBytes + FromBytes, | ||
>( | ||
expected: T, | ||
) { | ||
// Serialize | ||
let expected_string = &expected.to_string(); | ||
let candidate_string = serde_json::to_string(&expected).unwrap(); | ||
assert_eq!(expected_string, serde_json::Value::from_str(&candidate_string).unwrap().as_str().unwrap()); | ||
|
||
// Deserialize | ||
assert_eq!(expected, T::from_str(expected_string).unwrap_or_else(|_| panic!("FromStr: {expected_string}"))); | ||
assert_eq!(expected, serde_json::from_str(&candidate_string).unwrap()); | ||
} | ||
|
||
fn check_bincode< | ||
T: Serialize + for<'a> Deserialize<'a> + Debug + Display + PartialEq + Eq + FromStr + ToBytes + FromBytes, | ||
>( | ||
expected: T, | ||
) { | ||
// Serialize | ||
let expected_bytes = expected.to_bytes_le().unwrap(); | ||
let expected_bytes_with_size_encoding = bincode::serialize(&expected).unwrap(); | ||
assert_eq!(&expected_bytes[..], &expected_bytes_with_size_encoding[8..]); | ||
|
||
// Deserialize | ||
assert_eq!(expected, T::read_le(&expected_bytes[..]).unwrap()); | ||
assert_eq!(expected, bincode::deserialize(&expected_bytes_with_size_encoding[..]).unwrap()); | ||
} | ||
|
||
#[test] | ||
fn test_serde_json() { | ||
for case in TEST_CASES.iter() { | ||
check_serde_json(ArrayType::<Testnet3>::from_str(case).unwrap()); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_bincode() { | ||
for case in TEST_CASES.iter() { | ||
check_bincode(ArrayType::<Testnet3>::from_str(case).unwrap()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fd0390c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possible performance regression was detected for benchmark 'snarkVM Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold
1.50
.bls12_377: fq_double
12
ns/iter (± 0
)6
ns/iter (± 0
)2
This comment was automatically generated by workflow using github-action-benchmark.
CC: @raychu86