/ circuit / types / string / src / lib.rs
lib.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the alphavm library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  #![forbid(unsafe_code)]
 17  #![cfg_attr(test, allow(clippy::assertions_on_result_states))]
 18  
 19  extern crate alphavm_console_types_string as console;
 20  
 21  mod equal;
 22  mod helpers;
 23  
 24  #[cfg(test)]
 25  use alphavm_circuit_environment::assert_scope;
 26  #[cfg(test)]
 27  use console::TestRng;
 28  
 29  use alphavm_circuit_environment::prelude::*;
 30  use alphavm_circuit_types_boolean::Boolean;
 31  use alphavm_circuit_types_field::Field;
 32  use alphavm_circuit_types_integers::U8;
 33  
 34  #[derive(Clone)]
 35  pub struct StringType<E: Environment> {
 36      mode: Mode,
 37      bytes: Vec<U8<E>>,
 38      size_in_bytes: Field<E>,
 39  }
 40  
 41  impl<E: Environment> StringTrait for StringType<E> {}
 42  
 43  impl<E: Environment> Inject for StringType<E> {
 44      type Primitive = console::StringType<E::Network>;
 45  
 46      /// Initializes a new instance of a string.
 47      fn new(mode: Mode, string: Self::Primitive) -> Self {
 48          // Cast the number of bytes in the 'string' as a field element.
 49          let num_bytes =
 50              console::Field::from_u32(u32::try_from(string.len()).unwrap_or_else(|error| E::halt(error.to_string())));
 51  
 52          // "Load-bearing witness allocation - Please do not optimize me." - Pratyush :)
 53  
 54          // Inject the number of bytes as a constant.
 55          let expected_size_in_bytes = Field::constant(num_bytes);
 56          // Inject the number of bytes as a witness.
 57          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          // Ensure the witness matches the constant.
 62          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  
 72  impl<E: Environment> Eject for StringType<E> {
 73      type Primitive = console::StringType<E::Network>;
 74  
 75      /// Ejects the mode of the string.
 76      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      /// Ejects the string as a string literal.
 84      fn eject_value(&self) -> Self::Primitive {
 85          // Ensure the string is within the allowed capacity.
 86          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  impl<E: Environment> Parser for StringType<E> {
 98      /// Parses a string into a string circuit.
 99      #[inline]
100      fn parse(string: &str) -> ParserResult<Self> {
101          // Parse the content from the string.
102          let (string, content) = console::StringType::parse(string)?;
103          // Parse the mode from the string.
104          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  
113  impl<E: Environment> FromStr for StringType<E> {
114      type Err = Error;
115  
116      /// Parses a string into a string circuit.
117      #[inline]
118      fn from_str(string: &str) -> Result<Self> {
119          match Self::parse(string) {
120              Ok((remainder, object)) => {
121                  // Ensure the remainder is empty.
122                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
123                  // Return the object.
124                  Ok(object)
125              }
126              Err(error) => bail!("Failed to parse string. {error}"),
127          }
128      }
129  }
130  
131  impl<E: Environment> TypeName for StringType<E> {
132      /// Returns the type name of the circuit as a string.
133      #[inline]
134      fn type_name() -> &'static str {
135          console::StringType::<E::Network>::type_name()
136      }
137  }
138  
139  impl<E: Environment> Debug for StringType<E> {
140      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141          Display::fmt(self, f)
142      }
143  }
144  
145  impl<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  }