snarkvm_circuit_types_string/
lib.rs#![forbid(unsafe_code)]
#![cfg_attr(test, allow(clippy::assertions_on_result_states))]
mod equal;
mod helpers;
#[cfg(test)]
use console::TestRng;
#[cfg(test)]
use snarkvm_circuit_environment::assert_scope;
use snarkvm_circuit_environment::prelude::*;
use snarkvm_circuit_types_boolean::Boolean;
use snarkvm_circuit_types_field::Field;
use snarkvm_circuit_types_integers::U8;
#[derive(Clone)]
pub struct StringType<E: Environment> {
mode: Mode,
bytes: Vec<U8<E>>,
size_in_bytes: Field<E>,
}
impl<E: Environment> StringTrait for StringType<E> {}
#[cfg(feature = "console")]
impl<E: Environment> Inject for StringType<E> {
type Primitive = console::StringType<E::Network>;
fn new(mode: Mode, string: Self::Primitive) -> Self {
let num_bytes =
console::Field::from_u32(u32::try_from(string.len()).unwrap_or_else(|error| E::halt(error.to_string())));
let expected_size_in_bytes = Field::constant(num_bytes);
let size_in_bytes = match mode.is_constant() {
true => expected_size_in_bytes.clone(),
false => Field::new(Mode::Private, num_bytes),
};
E::assert_eq(&expected_size_in_bytes, &size_in_bytes);
Self {
mode,
bytes: string.as_bytes().iter().map(|byte| U8::new(mode, console::Integer::new(*byte))).collect(),
size_in_bytes,
}
}
}
#[cfg(feature = "console")]
impl<E: Environment> Eject for StringType<E> {
type Primitive = console::StringType<E::Network>;
fn eject_mode(&self) -> Mode {
match self.bytes.is_empty() {
true => self.mode,
false => self.bytes.eject_mode(),
}
}
fn eject_value(&self) -> Self::Primitive {
let num_bytes = self.bytes.len();
match num_bytes <= E::MAX_STRING_BYTES as usize {
true => console::StringType::new(
&String::from_utf8(self.bytes.eject_value().into_iter().map(|byte| *byte).collect())
.unwrap_or_else(|error| E::halt(format!("Failed to eject a string value: {error}"))),
),
false => E::halt(format!("Attempted to eject a string of size {num_bytes}")),
}
}
}
#[cfg(feature = "console")]
impl<E: Environment> Parser for StringType<E> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
let (string, content) = console::StringType::parse(string)?;
let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
match mode {
Some((_, mode)) => Ok((string, StringType::new(mode, content))),
None => Ok((string, StringType::new(Mode::Constant, content))),
}
}
}
#[cfg(feature = "console")]
impl<E: Environment> FromStr for StringType<E> {
type Err = Error;
#[inline]
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
#[cfg(feature = "console")]
impl<E: Environment> TypeName for StringType<E> {
#[inline]
fn type_name() -> &'static str {
console::StringType::<E::Network>::type_name()
}
}
#[cfg(feature = "console")]
impl<E: Environment> Debug for StringType<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
#[cfg(feature = "console")]
impl<E: Environment> Display for StringType<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{}", self.eject_value(), self.eject_mode())
}
}