snarkvm_circuit_types_string/
lib.rs1#![forbid(unsafe_code)]
17#![cfg_attr(test, allow(clippy::assertions_on_result_states))]
18
19extern crate snarkvm_console_types_string as console;
20
21mod equal;
22mod helpers;
23
24#[cfg(test)]
25use console::TestRng;
26#[cfg(test)]
27use snarkvm_circuit_environment::assert_scope;
28
29use snarkvm_circuit_environment::prelude::*;
30use snarkvm_circuit_types_boolean::Boolean;
31use snarkvm_circuit_types_field::Field;
32use snarkvm_circuit_types_integers::U8;
33
34#[derive(Clone)]
35pub struct StringType<E: Environment> {
36 mode: Mode,
37 bytes: Vec<U8<E>>,
38 size_in_bytes: Field<E>,
39}
40
41impl<E: Environment> StringTrait for StringType<E> {}
42
43impl<E: Environment> Inject for StringType<E> {
44 type Primitive = console::StringType<E::Network>;
45
46 fn new(mode: Mode, string: Self::Primitive) -> Self {
48 let num_bytes =
50 console::Field::from_u32(u32::try_from(string.len()).unwrap_or_else(|error| E::halt(error.to_string())));
51
52 let expected_size_in_bytes = Field::constant(num_bytes);
56 let size_in_bytes = match mode.is_constant() {
58 true => expected_size_in_bytes.clone(),
59 false => Field::new(Mode::Private, num_bytes),
60 };
61 E::assert_eq(&expected_size_in_bytes, &size_in_bytes);
63
64 Self {
65 mode,
66 bytes: string.as_bytes().iter().map(|byte| U8::new(mode, console::Integer::new(*byte))).collect(),
67 size_in_bytes,
68 }
69 }
70}
71
72impl<E: Environment> Eject for StringType<E> {
73 type Primitive = console::StringType<E::Network>;
74
75 fn eject_mode(&self) -> Mode {
77 match self.bytes.is_empty() {
78 true => self.mode,
79 false => self.bytes.eject_mode(),
80 }
81 }
82
83 fn eject_value(&self) -> Self::Primitive {
85 let num_bytes = self.bytes.len();
87 match num_bytes <= E::MAX_STRING_BYTES as usize {
88 true => console::StringType::new(
89 &String::from_utf8(self.bytes.eject_value().into_iter().map(|byte| *byte).collect())
90 .unwrap_or_else(|error| E::halt(format!("Failed to eject a string value: {error}"))),
91 ),
92 false => E::halt(format!("Attempted to eject a string of size {num_bytes}")),
93 }
94 }
95}
96
97impl<E: Environment> Parser for StringType<E> {
98 #[inline]
100 fn parse(string: &str) -> ParserResult<Self> {
101 let (string, content) = console::StringType::parse(string)?;
103 let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
105
106 match mode {
107 Some((_, mode)) => Ok((string, StringType::new(mode, content))),
108 None => Ok((string, StringType::new(Mode::Constant, content))),
109 }
110 }
111}
112
113impl<E: Environment> FromStr for StringType<E> {
114 type Err = Error;
115
116 #[inline]
118 fn from_str(string: &str) -> Result<Self> {
119 match Self::parse(string) {
120 Ok((remainder, object)) => {
121 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
123 Ok(object)
125 }
126 Err(error) => bail!("Failed to parse string. {error}"),
127 }
128 }
129}
130
131impl<E: Environment> TypeName for StringType<E> {
132 #[inline]
134 fn type_name() -> &'static str {
135 console::StringType::<E::Network>::type_name()
136 }
137}
138
139impl<E: Environment> Debug for StringType<E> {
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141 Display::fmt(self, f)
142 }
143}
144
145impl<E: Environment> Display for StringType<E> {
146 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147 write!(f, "{}.{}", self.eject_value(), self.eject_mode())
148 }
149}