snarkvm_circuit_types_string/
lib.rs1#![forbid(unsafe_code)]
17#![cfg_attr(test, allow(clippy::assertions_on_result_states))]
18
19mod equal;
20mod helpers;
21
22#[cfg(test)]
23use console::TestRng;
24#[cfg(test)]
25use snarkvm_circuit_environment::assert_scope;
26
27use snarkvm_circuit_environment::prelude::*;
28use snarkvm_circuit_types_boolean::Boolean;
29use snarkvm_circuit_types_field::Field;
30use snarkvm_circuit_types_integers::U8;
31
32#[derive(Clone)]
33pub struct StringType<E: Environment> {
34 mode: Mode,
35 bytes: Vec<U8<E>>,
36 size_in_bytes: Field<E>,
37}
38
39impl<E: Environment> StringTrait for StringType<E> {}
40
41#[cfg(feature = "console")]
42impl<E: Environment> Inject for StringType<E> {
43 type Primitive = console::StringType<E::Network>;
44
45 fn new(mode: Mode, string: Self::Primitive) -> Self {
47 let num_bytes =
49 console::Field::from_u32(u32::try_from(string.len()).unwrap_or_else(|error| E::halt(error.to_string())));
50
51 let expected_size_in_bytes = Field::constant(num_bytes);
55 let size_in_bytes = match mode.is_constant() {
57 true => expected_size_in_bytes.clone(),
58 false => Field::new(Mode::Private, num_bytes),
59 };
60 E::assert_eq(&expected_size_in_bytes, &size_in_bytes);
62
63 Self {
64 mode,
65 bytes: string.as_bytes().iter().map(|byte| U8::new(mode, console::Integer::new(*byte))).collect(),
66 size_in_bytes,
67 }
68 }
69}
70
71#[cfg(feature = "console")]
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
97#[cfg(feature = "console")]
98impl<E: Environment> Parser for StringType<E> {
99 #[inline]
101 fn parse(string: &str) -> ParserResult<Self> {
102 let (string, content) = console::StringType::parse(string)?;
104 let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
106
107 match mode {
108 Some((_, mode)) => Ok((string, StringType::new(mode, content))),
109 None => Ok((string, StringType::new(Mode::Constant, content))),
110 }
111 }
112}
113
114#[cfg(feature = "console")]
115impl<E: Environment> FromStr for StringType<E> {
116 type Err = Error;
117
118 #[inline]
120 fn from_str(string: &str) -> Result<Self> {
121 match Self::parse(string) {
122 Ok((remainder, object)) => {
123 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
125 Ok(object)
127 }
128 Err(error) => bail!("Failed to parse string. {error}"),
129 }
130 }
131}
132
133#[cfg(feature = "console")]
134impl<E: Environment> TypeName for StringType<E> {
135 #[inline]
137 fn type_name() -> &'static str {
138 console::StringType::<E::Network>::type_name()
139 }
140}
141
142#[cfg(feature = "console")]
143impl<E: Environment> Debug for StringType<E> {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 Display::fmt(self, f)
146 }
147}
148
149#[cfg(feature = "console")]
150impl<E: Environment> Display for StringType<E> {
151 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
152 write!(f, "{}.{}", self.eject_value(), self.eject_mode())
153 }
154}