diff --git a/Cargo.lock b/Cargo.lock index be38cf1..1d862d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,19 +534,18 @@ dependencies = [ [[package]] name = "strict_encoding" version = "2.7.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12eaa6985afa31deacc86cdc4935a36960ae09131a1f4e1db430127ebc4f05d" +source = "git+https://github.com/strict-types/strict-encoding?branch=rstring#49d4a966c3463232bdf5912272317469af1d7109" dependencies = [ "amplify", "half", + "serde", "strict_encoding_derive", ] [[package]] name = "strict_encoding_derive" version = "2.7.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1b064a62618a785e6d8f4df13d905dc335b56400d48f9b4f8b12dcba82b260" +source = "git+https://github.com/strict-types/strict-encoding?branch=rstring#49d4a966c3463232bdf5912272317469af1d7109" dependencies = [ "amplify_syn", "heck", @@ -558,8 +557,7 @@ dependencies = [ [[package]] name = "strict_types" version = "2.7.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c32716de4b99b0e8fb0c114e99b6929613e8d7302999c6b8c77251783923ad" +source = "git+https://github.com/strict-types/strict-types?branch=rstring#53621541113829fd3ba4fa2d63ca889b71072200" dependencies = [ "amplify", "ascii-armor", diff --git a/Cargo.toml b/Cargo.toml index 6f75bee..8f4ba3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ std = ["amplify/std"] log = ["std"] alloc = ["amplify/alloc"] curve25519 = ["curve25519-dalek"] -serde = ["serde_crate", "amplify/serde", "std"] +serde = ["serde_crate", "amplify/serde", "std", "strict_encoding/serde"] [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2" @@ -55,3 +55,8 @@ wasm-bindgen-test = "0.3" [package.metadata.docs.rs] features = ["all"] + +[patch.crates-io] +strict_encoding_derive = { git = "https://github.com/strict-types/strict-encoding", branch = "rstring" } +strict_encoding = { git = "https://github.com/strict-types/strict-encoding", branch = "rstring" } +strict_types = { git = "https://github.com/strict-types/strict-types", branch = "rstring" } diff --git a/src/data/encoding.rs b/src/data/encoding.rs index 0049acf..0158d6d 100644 --- a/src/data/encoding.rs +++ b/src/data/encoding.rs @@ -28,13 +28,11 @@ use std::iter::FromIterator; use std::marker::PhantomData; use std::string::FromUtf8Error; -use amplify::{IoError, Wrapper}; +use amplify::{confinement, IoError, Wrapper}; use crate::data::encoding::DecodeError::InvalidBool; use crate::data::{ByteStr, FloatLayout, IntLayout, Layout, MaybeNumber, Number, NumberLayout}; -use crate::library::{ - IsaSeg, IsaSegError, Lib, LibId, LibSeg, LibSegOverflow, LibSite, SegmentError, -}; +use crate::library::{IsaSegError, LibId, LibSite, SegmentError}; /// Trait for encodable container data structures used by AluVM and runtime environments pub trait Encode { @@ -126,7 +124,7 @@ pub enum DecodeError { /// Library segment construction error #[display(inner)] #[from] - LibSeg(LibSegOverflow), + LibSeg(confinement::Error), /// ISAE segment construction error #[display(inner)] @@ -616,46 +614,6 @@ impl Decode for LibId { } } -impl Encode for IsaSeg { - type Error = EncodeError; - - fn encode(&self, writer: impl Write) -> Result { - self.to_string().encode(writer) - } -} - -impl Decode for IsaSeg { - type Error = DecodeError; - - fn decode(reader: impl Read) -> Result - where - Self: Sized, - { - let s = String::decode(reader)?; - IsaSeg::with(s).map_err(DecodeError::from) - } -} - -impl Encode for LibSeg { - type Error = EncodeError; - - fn encode(&self, writer: impl Write) -> Result { - MaxLenByte::new(self).encode(writer) - } -} - -impl Decode for LibSeg { - type Error = DecodeError; - - fn decode(reader: impl Read) -> Result - where - Self: Sized, - { - let seg: Vec<_> = MaxLenByte::decode(reader)?.release(); - Ok(LibSeg::from_iter(seg)?) - } -} - impl Encode for LibSite { type Error = io::Error; @@ -676,30 +634,3 @@ impl Decode for LibSite { Ok(LibSite::with(pos, id)) } } - -impl Encode for Lib { - type Error = EncodeError; - - fn encode(&self, mut writer: impl Write) -> Result { - Ok(self.isae_segment().encode(&mut writer)? - + self.code.encode(&mut writer)? - + self.data.encode(&mut writer)? - + self.libs.encode(&mut writer)?) - } -} - -impl Decode for Lib { - type Error = DecodeError; - - fn decode(mut reader: impl Read) -> Result - where - Self: Sized, - { - Ok(Lib::with( - String::decode(&mut reader)?.as_str(), - ByteStr::decode(&mut reader)?.to_vec(), - ByteStr::decode(&mut reader)?.to_vec(), - LibSeg::decode(&mut reader)?, - )?) - } -} diff --git a/src/isa/exec.rs b/src/isa/exec.rs index 114af11..0795b90 100644 --- a/src/isa/exec.rs +++ b/src/isa/exec.rs @@ -39,7 +39,7 @@ use super::{ }; use crate::data::{ByteStr, MaybeNumber, Number, NumberLayout}; use crate::isa::{ExtendFlag, FloatEqFlag, IntFlags, MergeFlag, NoneEqFlag, SignFlag}; -use crate::library::{constants, LibSite}; +use crate::library::{constants, IsaName, IsaSeg, LibSite}; use crate::reg::{CoreRegs, NumericRegister, Reg, Reg32, RegA, RegA2, RegAR, RegBlockAR, RegR}; /// Turing machine movement after instruction execution @@ -67,13 +67,13 @@ pub trait InstructionSet: Bytecode + core::fmt::Display + core::fmt::Debug { /// /// Each id must be up to 8 bytes and consist of upper case latin alphanumeric characters, /// starting with non-number. - fn isa_ids() -> BTreeSet<&'static str>; + fn isa_ids() -> IsaSeg; /// ISA Extension IDs represented as a standard string (space-separated) /// /// Concatenated length of the ISA IDs joined via ' ' character must not exceed 128 bytes. #[inline] - fn isa_string() -> String { Self::isa_ids().into_iter().collect::>().join(" ") } + fn isa_string() -> String { Self::isa_ids().to_string() } /// ISA Extension IDs encoded in a standard way (space-separated) /// @@ -83,7 +83,7 @@ pub trait InstructionSet: Bytecode + core::fmt::Display + core::fmt::Debug { /// Checks whether provided ISA extension ID is supported by the current instruction set #[inline] - fn is_supported(id: &str) -> bool { Self::isa_ids().contains(id) } + fn is_supported(id: &IsaName) -> bool { Self::isa_ids().contains(id) } /// Lists all registers which are used by the instruction. fn regs(&self) -> BTreeSet { @@ -123,13 +123,12 @@ where type Context<'ctx> = Extension::Context<'ctx>; #[inline] - fn isa_ids() -> BTreeSet<&'static str> { - let mut set = BTreeSet::new(); - set.insert(constants::ISA_ID_ALU); - set.extend(DigestOp::isa_ids()); - set.extend(Secp256k1Op::isa_ids()); - set.extend(Curve25519Op::isa_ids()); - set.extend(Extension::isa_ids()); + fn isa_ids() -> IsaSeg { + let mut set = IsaSeg::with(constants::ISA_ID_ALU); + set.extend(DigestOp::isa_ids()).expect("hardcoded"); + set.extend(Secp256k1Op::isa_ids()).expect("hardcoded"); + set.extend(Curve25519Op::isa_ids()).expect("hardcoded"); + set.extend(Extension::isa_ids()).expect("hardcoded"); set } @@ -199,7 +198,7 @@ impl InstructionSet for ControlFlowOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } fn src_regs(&self) -> BTreeSet { bset![] } @@ -246,7 +245,7 @@ impl InstructionSet for PutOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } fn src_regs(&self) -> BTreeSet { bset![] } @@ -309,7 +308,7 @@ impl InstructionSet for MoveOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } fn src_regs(&self) -> BTreeSet { match self { @@ -493,7 +492,7 @@ impl InstructionSet for CmpOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } fn src_regs(&self) -> BTreeSet { match self { @@ -647,7 +646,7 @@ impl InstructionSet for ArithmeticOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } fn src_regs(&self) -> BTreeSet { match self { @@ -812,7 +811,7 @@ impl InstructionSet for BitwiseOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } fn src_regs(&self) -> BTreeSet { match self { @@ -1043,7 +1042,7 @@ impl InstructionSet for BytesOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } fn src_regs(&self) -> BTreeSet { match self { @@ -1362,11 +1361,7 @@ impl InstructionSet for DigestOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { - let mut set = BTreeSet::new(); - set.insert(constants::ISA_ID_BPDIGEST); - set - } + fn isa_ids() -> IsaSeg { IsaSeg::with(constants::ISA_ID_BPDIGEST) } fn src_regs(&self) -> BTreeSet { match self { @@ -1434,15 +1429,11 @@ impl InstructionSet for Secp256k1Op { #[cfg(not(feature = "secp256k1"))] #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } #[cfg(feature = "secp256k1")] #[inline] - fn isa_ids() -> BTreeSet<&'static str> { - let mut set = BTreeSet::new(); - set.insert(constants::ISA_ID_SECP256K); - set - } + fn isa_ids() -> IsaSeg { IsaSeg::with(constants::ISA_ID_SECP256K) } fn src_regs(&self) -> BTreeSet { match self { @@ -1581,15 +1572,11 @@ impl InstructionSet for Curve25519Op { #[cfg(not(feature = "curve25519"))] #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } #[cfg(feature = "curve25519")] #[inline] - fn isa_ids() -> BTreeSet<&'static str> { - let mut set = BTreeSet::new(); - set.insert(constants::ISA_ID_ED25519); - set - } + fn isa_ids() -> IsaSeg { IsaSeg::with(constants::ISA_ID_ED25519) } fn src_regs(&self) -> BTreeSet { match self { @@ -1646,7 +1633,7 @@ impl InstructionSet for ReservedOp { type Context<'ctx> = (); #[inline] - fn isa_ids() -> BTreeSet<&'static str> { BTreeSet::default() } + fn isa_ids() -> IsaSeg { IsaSeg::default() } fn src_regs(&self) -> BTreeSet { bset![] } diff --git a/src/library/constants.rs b/src/library/constants.rs index bba00f6..4cb9a81 100644 --- a/src/library/constants.rs +++ b/src/library/constants.rs @@ -25,35 +25,24 @@ #![allow(missing_docs)] -pub const CODE_SEGMENT_MAX_LEN: usize = 1 << 16; +pub const CODE_SEGMENT_MAX_LEN: usize = 0xFFFF; -pub const DATA_SEGMENT_MAX_LEN: usize = 1 << 16; +pub const DATA_SEGMENT_MAX_LEN: usize = 0xFFFF; /// Maximum number of libraries that may be referenced (used by) any other library; i.e. limit for /// the number of records inside program segment. -pub const LIBS_SEGMENT_MAX_COUNT: usize = 1 << 8; +pub const LIBS_SEGMENT_MAX_COUNT: usize = 0xFF; /// Maximum total number of libraries which may be used by a single program; i.e. maximal number of /// nodes in a library dependency tree. pub const LIBS_MAX_TOTAL: u16 = 1024; -pub const ISAE_SEGMENT_MAX_LEN: usize = 0xFF; - -pub const ISAE_SEGMENT_MAX_COUNT: usize = 32; +pub const ISAE_SEGMENT_MAX_COUNT: usize = 64; pub const ISA_ID_MIN_LEN: usize = 2; pub const ISA_ID_MAX_LEN: usize = 8; -pub const ISA_ID_ALLOWED_CHARS: [char; 36] = [ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', - 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', -]; -pub const ISA_ID_ALLOWED_FIRST_CHAR: [char; 26] = [ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', - 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', -]; - pub const ISA_ID_ALU: &str = "ALU"; pub const ISA_ID_BPDIGEST: &str = "BPDIGEST"; pub const ISA_ID_SECP256K: &str = "SECP256K"; diff --git a/src/library/lib.rs b/src/library/lib.rs index 85e3c23..dfec966 100644 --- a/src/library/lib.rs +++ b/src/library/lib.rs @@ -26,14 +26,15 @@ use alloc::string::{String, ToString}; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; use core::cmp::Ordering; -use core::convert::TryFrom; use core::fmt::{self, Display, Formatter}; use core::hash::{Hash as RustHash, Hasher}; use core::str::FromStr; -use amplify::{ByteArray, Bytes32}; +use amplify::confinement::SmallBlob; +use amplify::{confinement, ByteArray, Bytes32}; use baid58::{Baid58ParseError, FromBaid58, ToBaid58}; use sha2::{Digest, Sha256}; +use strict_encoding::{StrictDeserialize, StrictSerialize}; #[cfg(feature = "ascii-armor")] pub use self::_armor::LibArmorError; @@ -41,7 +42,7 @@ use super::{Cursor, Read}; use crate::data::ByteStr; use crate::isa::{Bytecode, BytecodeError, ExecStep, Instr, InstructionSet}; use crate::library::segs::IsaSeg; -use crate::library::{CodeEofError, LibSeg, LibSegOverflow, SegmentError}; +use crate::library::{CodeEofError, LibSeg, SegmentError}; use crate::reg::CoreRegs; use crate::LIB_NAME_ALUVM; @@ -122,24 +123,29 @@ impl LibId { /// AluVM executable code library #[derive(Clone, Debug, Default)] -// #[cfg_attr(feature = "strict_encoding", derive(StrictEncode, StrictDecode))] +#[derive(StrictType, StrictDecode)] +#[cfg_attr(feature = "std", derive(StrictEncode))] +#[strict_type(lib = LIB_NAME_ALUVM)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] pub struct Lib { /// ISA segment pub isae: IsaSeg, /// Code segment - pub code: ByteStr, + pub code: SmallBlob, /// Data segment - pub data: ByteStr, + pub data: SmallBlob, /// Libs segment pub libs: LibSeg, } +impl StrictSerialize for Lib {} +impl StrictDeserialize for Lib {} + impl Display for Lib { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln!(f, "ISAE: {}", &self.isae)?; - write!(f, "CODE:\n{:#10}", self.code)?; - write!(f, "DATA:\n{:#10}", self.data)?; + write!(f, "CODE:\n{:#10}", ByteStr::with(self.code.as_ref()))?; + write!(f, "DATA:\n{:#10}", ByteStr::with(self.data.as_ref()))?; if self.libs.count() > 0 { write!(f, "LIBS: {:8}", self.libs) } else { @@ -172,10 +178,11 @@ impl RustHash for Lib { #[cfg(feature = "ascii-armor")] mod _armor { + use amplify::confinement::{self, Confined, U24 as U24MAX}; use armor::{ArmorHeader, ArmorParseError, AsciiArmor, ASCII_ARMOR_ID}; + use strict_encoding::DeserializeError; use super::*; - use crate::data::encoding::{Decode, DecodeError, Encode}; const ASCII_ARMOR_ISAE: &str = "ISA-Extensions"; const ASCII_ARMOR_DEPENDENCY: &str = "Dependency"; @@ -188,9 +195,13 @@ mod _armor { #[from] Armor(ArmorParseError), + /// The provided data exceed maximum possible library size. + #[from(confinement::Error)] + TooLarge, + /// Library data deserialization error. #[from] - Decode(DecodeError), + Decode(DeserializeError), } impl AsciiArmor for Lib { @@ -208,18 +219,21 @@ mod _armor { headers } - fn to_ascii_armored_data(&self) -> Vec { self.serialize() } + fn to_ascii_armored_data(&self) -> Vec { + self.to_strict_serialized::().expect("type guarantees").to_vec() + } fn with_headers_data(_headers: Vec, data: Vec) -> Result { // TODO: check id, dependencies and ISAE - let me = Self::deserialize(data)?; + let data = Confined::try_from(data)?; + let me = Self::from_strict_serialized::(data)?; Ok(me) } } } /// Errors while assembling library from the instruction set -#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)] +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Display, From)] #[display(inner)] pub enum AssemblerError { /// Error assembling code and data segments @@ -228,7 +242,7 @@ pub enum AssemblerError { /// Error assembling library segment #[from] - LibSegOverflow(LibSegOverflow), + LibSegOverflow(confinement::Error), } #[cfg(feature = "std")] @@ -249,14 +263,14 @@ impl Lib { data: Vec, libs: LibSeg, ) -> Result { - let isae = IsaSeg::from_iter(isa.split(' '))?; + let isae = IsaSeg::from_str(isa)?; + let len = bytecode.len(); Ok(Self { isae, libs, - code: ByteStr::try_from(bytecode.as_slice()) - .map_err(|_| SegmentError::CodeSegmentTooLarge(bytecode.len()))?, - data: ByteStr::try_from(data.as_slice()) - .map_err(|_| SegmentError::DataSegmentTooLarge(bytecode.len()))?, + code: SmallBlob::try_from(bytecode) + .map_err(|_| SegmentError::CodeSegmentTooLarge(len))?, + data: SmallBlob::try_from(data).map_err(|_| SegmentError::DataSegmentTooLarge(len))?, }) } @@ -265,25 +279,18 @@ impl Lib { where Isa: InstructionSet, { - let call_sites = code.iter().filter_map(|instr| instr.call_site()); - let libs_segment = LibSeg::with(call_sites)?; + let call_sites = code.iter().filter_map(|instr| instr.call_site()).map(|site| site.lib); + let libs_segment = LibSeg::try_from_iter(call_sites)?; let mut code_segment = ByteStr::default(); let mut writer = Cursor::<_, ByteStr>::new(&mut code_segment.bytes[..], &libs_segment); for instr in code.iter() { instr.encode(&mut writer)?; } - let pos = writer.pos(); - let data_segment = writer.into_data_segment(); - code_segment.adjust_len(pos); - - Ok(Lib { - isae: IsaSeg::from_iter(Isa::isa_ids()) - .expect("ISA instruction set contains incorrect ISAE ids"), - libs: libs_segment, - code: code_segment, - data: data_segment, - }) + let data_segment = SmallBlob::from_collection_unsafe(writer.into_data_segment().to_vec()); + let code_segment = SmallBlob::from_collection_unsafe(code_segment.to_vec()); + + Ok(Lib { isae: Isa::isa_ids(), libs: libs_segment, code: code_segment, data: data_segment }) } /// Disassembles library into a set of instructions diff --git a/src/library/mod.rs b/src/library/mod.rs index 6114ab5..c51531a 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -34,4 +34,4 @@ pub use cursor::Cursor; pub use lib::LibArmorError; pub use lib::{AssemblerError, Lib, LibId, LibSite}; pub use rw::{CodeEofError, Read, Write, WriteError}; -pub use segs::{IsaSeg, IsaSegError, LibSeg, LibSegOverflow, SegmentError}; +pub use segs::{IsaName, IsaSeg, IsaSegError, LibSeg, SegmentError}; diff --git a/src/library/segs.rs b/src/library/segs.rs index b4184ec..eee9793 100644 --- a/src/library/segs.rs +++ b/src/library/segs.rs @@ -25,21 +25,29 @@ #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::borrow::ToOwned; -use alloc::collections::{BTreeMap, BTreeSet}; +use alloc::collections::BTreeSet; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::string::String; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec; -use core::fmt::{self, Display, Formatter}; +use core::fmt::{self, Debug, Display, Formatter}; +use core::str::FromStr; +use std::collections::btree_set; +use std::convert::TryFrom; + +use amplify::confinement; +use amplify::confinement::Confined; +use strict_encoding::stl::{AlphaCaps, AlphaCapsNum}; +use strict_encoding::{InvalidRString, RString}; use crate::library::constants::{ - ISAE_SEGMENT_MAX_COUNT, ISAE_SEGMENT_MAX_LEN, ISA_ID_ALLOWED_CHARS, ISA_ID_ALLOWED_FIRST_CHAR, - ISA_ID_MAX_LEN, ISA_ID_MIN_LEN, LIBS_SEGMENT_MAX_COUNT, + ISAE_SEGMENT_MAX_COUNT, ISA_ID_MAX_LEN, ISA_ID_MIN_LEN, LIBS_SEGMENT_MAX_COUNT, }; -use crate::library::{LibId, LibSite}; +use crate::library::LibId; +use crate::LIB_NAME_ALUVM; /// Errors while processing binary-encoded segment data -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)] +#[derive(Clone, Eq, PartialEq, Hash, Debug, Display, From)] #[cfg_attr(feature = "std", derive(Error))] #[display(doc_comments)] pub enum SegmentError { @@ -56,44 +64,47 @@ pub enum SegmentError { } /// Errors while processing ISA extensions segment -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, Display, From)] #[cfg_attr(feature = "std", derive(Error))] #[display(doc_comments)] pub enum IsaSegError { - /// the size of ISAE (instruction set extensions) segment is {0}, which exceeds - /// [`ISAE_SEGMENT_MAX_LEN`] - SegmentTooLarge(usize), - - /// number of ISA ids in ISAE segment is {0}, which exceeds [`ISAE_SEGMENT_MAX_COUNT`] - SegmentTooManyExt(usize), - - /// ISA id {0} has a wrong length outside of [`ISA_ID_MIN_LEN`]`..=`[`ISA_ID_MAX_LEN`] bounds - IsaIdWrongLength(String), + /// ISA segment is invalid, specifically {0} + #[from] + Number(confinement::Error), - /// ISA id {0} includes wrong symbols (must contain only uppercase alphanumeric and start with - /// letter) - IsaIdWrongSymbols(String), + /// ISA id {0} has a wrong name, specifically {1} + Name(String, InvalidRString), } -/// ISA extensions segment -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] -// #[cfg_attr(feature = "strict_encoding", derive(StrictEncode, StrictDecode))] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct IsaSeg(BTreeSet); - -impl IsaSeg { - /// Returns iterator over unique ISA ids iterated in the deterministic (lexicographic) order - #[inline] - pub fn iter(&self) -> ::alloc::collections::btree_set::Iter { self.0.iter() } -} - -impl<'a> IntoIterator for &'a IsaSeg { - type Item = &'a String; - type IntoIter = ::alloc::collections::btree_set::Iter<'a, String>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { self.0.iter() } -} +/// ISA extension name. +#[derive(Wrapper, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, From)] +#[wrapper(Deref, Display, FromStr)] +#[derive(StrictDumb, StrictType, StrictDecode)] +#[cfg_attr(feature = "std", derive(StrictEncode))] +#[strict_type(lib = LIB_NAME_ALUVM)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct IsaName(RString); + +impl_ident_type!(IsaName); +impl_ident_subtype!(IsaName); + +/// ISA extensions segment. +#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] +#[wrapper(Deref)] +#[wrapper_mut(DerefMut)] +#[derive(StrictType, StrictDecode)] +#[cfg_attr(feature = "std", derive(StrictEncode))] +#[strict_type(lib = LIB_NAME_ALUVM)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct IsaSeg(Confined, 0, ISAE_SEGMENT_MAX_COUNT>); impl IsaSeg { /// Constructs ISAE segment from a string. @@ -106,72 +117,55 @@ impl IsaSeg { /// Errors with [`IsaSegError`] if the segment can't be correctly constructed from the probided /// data. #[inline] - pub fn with(s: impl AsRef) -> Result { - IsaSeg::from_iter(s.as_ref().split(' ')) - } - - /// Constructs ISAE segment from an iterator over correct ISA id strings. - /// - /// ISAE segment deterministically orders ISAE ids lexicographically. This is not a requirement, - /// but just a good practice for producing the same code on different platforms. - /// - /// # Error - /// - /// Errors with [`IsaSegError`] if the segment can't be correctly constructed from the provided - /// data. - #[allow(clippy::should_implement_trait)] - pub fn from_iter( - source: impl IntoIterator>, - ) -> Result { - let isa_codes = - source.into_iter().map(|s| s.as_ref().trim().to_owned()).collect::>(); - if isa_codes.len() > ISAE_SEGMENT_MAX_COUNT { - return Err(IsaSegError::SegmentTooManyExt(isa_codes.len())); - } - let mut total_len = 0usize; - for isae in &isa_codes { - if !(ISA_ID_MIN_LEN..=ISA_ID_MAX_LEN).contains(&isae.len()) { - return Err(IsaSegError::IsaIdWrongLength(isae.to_owned())); - } - if isae.chars().any(|ch| !ISA_ID_ALLOWED_CHARS.contains(&ch)) - || isae - .chars() - .next() - .map(|ch| !ISA_ID_ALLOWED_FIRST_CHAR.contains(&ch)) - .unwrap_or_default() - { - return Err(IsaSegError::IsaIdWrongSymbols(isae.to_owned())); - } - total_len += isae.len(); - } - if total_len > ISAE_SEGMENT_MAX_LEN { - return Err(IsaSegError::SegmentTooLarge(total_len)); - } - Ok(IsaSeg(isa_codes)) + pub fn with(s: &'static str) -> Self { + Self::from_str(s).expect("invalid hardcoded ISA extension name") } /// Returns number of ISA extensions in the ISAE segment #[inline] pub fn count(&self) -> u8 { self.0.len() as u8 } - /// Returns specific ISA id with a given index in the segment + /// Returns specific ISA id with a given index in the segment. #[inline] - pub fn at(&self, index: u8) -> Option { + pub fn at(&self, index: u8) -> Option { self.0.iter().enumerate().nth(index as usize).map(|(_, isa)| isa).cloned() } + + /// Constructs ISA segment from an iterator over ISA extension names. + #[inline] + pub fn try_from_iter( + iter: impl IntoIterator, + ) -> Result { + Confined::try_from_iter(iter).map(Self) + } +} + +impl IntoIterator for IsaSeg { + type Item = IsaName; + type IntoIter = btree_set::IntoIter; + + fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl Display for IsaSeg { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.0.iter().cloned().collect::>().join(" ")) + write!(f, "{}", self.0.iter().map(IsaName::to_string).collect::>().join(" ")) } } -/// unable to add a library to the library segment: maximum number of libraries (2^16) exceeded -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, Display)] -#[cfg_attr(feature = "std", derive(Error))] -#[display(doc_comments)] -pub struct LibSegOverflow; +impl FromStr for IsaSeg { + type Err = IsaSegError; + + fn from_str(s: &str) -> Result { + let mut seg = Confined::, 0, ISAE_SEGMENT_MAX_COUNT>::new(); + for isa in s.split(' ') { + let name = + IsaName::from_str(isa).map_err(|err| IsaSegError::Name(isa.to_owned(), err))?; + seg.push(name)?; + } + Ok(Self(seg)) + } +} /// Library segment data keeping collection of libraries which MAY be used in some program. /// Libraries are referenced in the bytecode using 16-bit position number in this index. @@ -193,17 +187,18 @@ pub struct LibSegOverflow; /// The implementation MUST ensure that the size of the index never exceeds `u16::MAX`. /// /// [`LIBS_MAX_TOTAL`]: super::constants::LIBS_MAX_TOTAL -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] -// #[cfg_attr(feature = "strict_encoding", derive(StrictEncode, StrictDecode))] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))] -pub struct LibSeg { - /// Set maintains unique library ids which may be iterated in lexicographic ordering - set: BTreeSet, - - /// Table matches lexicographic-based library index to the library id (i.e. this is reverse - /// index). - table: BTreeMap, -} +#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default, From)] +#[wrapper(Deref)] +#[wrapper_mut(DerefMut)] +#[derive(StrictType, StrictDecode)] +#[cfg_attr(feature = "std", derive(StrictEncode))] +#[strict_type(lib = LIB_NAME_ALUVM)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(crate = "serde_crate", transparent) +)] +pub struct LibSeg(Confined, 0, LIBS_SEGMENT_MAX_COUNT>); impl LibSeg { /// Returns iterator over unique libraries iterated in the deterministic (lexicographic) order @@ -216,51 +211,17 @@ impl<'a> IntoIterator for &'a LibSeg { type IntoIter = ::alloc::collections::btree_set::Iter<'a, LibId>; #[inline] - fn into_iter(self) -> Self::IntoIter { self.set.iter() } + fn into_iter(self) -> Self::IntoIter { self.0.iter() } } impl LibSeg { - /// Constructs program segment from an iterator over call locations. - /// - /// Lib segment deterministically orders library ids according to their [`LibId`] `Ord` - /// implementation. This is not a requirement, but just a good practice for producing the same - /// code on different platforms. - /// - /// # Error - /// - /// Errors with [`LibSegOverflow`] if the number of unique library ids exceeds - /// [`LIBS_SEGMENT_MAX_COUNT`]. - pub fn with(source: impl IntoIterator) -> Result { - LibSeg::from_iter(source.into_iter().map(|site| site.lib)) - } - - /// Constructs program segment from an iterator over lib ids. - /// - /// Lib segment deterministically orders library ids according to their [`LibId`] `Ord` - /// implementation. This is not a requirement, but just a good practice for producing the same - /// code on different platforms. - /// - /// # Error - /// - /// Errors with [`LibSegOverflow`] if the number of unique library ids exceeds - /// [`LIBS_SEGMENT_MAX_COUNT`]. - #[allow(clippy::should_implement_trait)] - pub fn from_iter(source: impl IntoIterator) -> Result { - let set = source.into_iter().collect::>(); - if set.len() > LIBS_SEGMENT_MAX_COUNT { - return Err(LibSegOverflow); - } - let table = set.iter().enumerate().map(|(index, id)| (index as u8, *id)).collect(); - Ok(LibSeg { set, table }) - } - /// Returns number of libraries in the lib segment #[inline] - pub fn count(&self) -> u8 { self.set.len() as u8 } + pub fn count(&self) -> u8 { self.0.len() as u8 } /// Returns library id with a given index #[inline] - pub fn at(&self, index: u8) -> Option { self.table.get(&index).copied() } + pub fn at(&self, index: u8) -> Option { self.0.iter().nth(index as usize).copied() } /// Returns index of a library. /// @@ -272,37 +233,21 @@ impl LibSeg { /// If the library is not present in program segment, returns `None`. #[inline] pub fn index(&self, lib: LibId) -> Option { - self.set.iter().position(|l| *l == lib).map(|i| i as u8) + self.0.iter().position(|l| *l == lib).map(|i| i as u8) } - /// Adds library id to the library segment. - /// - /// # Errors - /// - /// Checks requirement that the total number of libraries must not exceed `LIBS_MAX_TOTAL` and - /// returns [`LibSegOverflow`] otherwise - /// - /// # Returns - /// - /// `true` if the library was already known and `false` otherwise. + /// Constructs libraries segment from an iterator over library ids. #[inline] - pub fn add_lib(&mut self, id: LibId) -> Result { - if self.set.len() >= LIBS_SEGMENT_MAX_COUNT { - Err(LibSegOverflow) - } else if self.index(id).is_some() { - Ok(true) - } else { - self.set.insert(id); - let pos = self.index(id).expect("library inserted into a set is absent in the set"); - self.table.insert(pos, id); - Ok(false) - } + pub fn try_from_iter( + iter: impl IntoIterator, + ) -> Result { + Confined::try_from_iter(iter).map(Self) } } impl Display for LibSeg { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - self.set.iter().enumerate().try_for_each(|(line, lib)| { + self.0.iter().enumerate().try_for_each(|(line, lib)| { writeln!( f, "{:>2$}{}", diff --git a/src/program.rs b/src/program.rs index aa8bae3..742a867 100644 --- a/src/program.rs +++ b/src/program.rs @@ -30,7 +30,7 @@ use core::marker::PhantomData; use crate::isa::InstructionSet; use crate::library::constants::LIBS_MAX_TOTAL; -use crate::library::{Lib, LibId, LibSite}; +use crate::library::{IsaName, Lib, LibId, LibSite}; /// Trait for a concrete program implementation provided by a runtime environment. pub trait Program { @@ -62,7 +62,7 @@ pub trait Program { #[display(doc_comments)] pub enum ProgError { /// ISA id {0} is not supported by the selected instruction set - IsaNotSupported(String), + IsaNotSupported(IsaName), /// Attempt to add library when maximum possible number of libraries is already present in /// the VM @@ -147,7 +147,7 @@ where if self.lib_count() >= LIBS_MAX_TOTAL.min(Self::RUNTIME_MAX_TOTAL_LIBS) { return Err(ProgError::TooManyLibs); } - for isa in &lib.isae { + for isa in lib.isae.iter() { if !Isa::is_supported(isa) { return Err(ProgError::IsaNotSupported(isa.to_owned())); } diff --git a/src/stl.rs b/src/stl.rs index af1932d..8e0d202 100644 --- a/src/stl.rs +++ b/src/stl.rs @@ -23,21 +23,25 @@ //! Strict types library generator methods. +use core::convert::TryFrom; + use strict_types::typelib::{CompileError, LibBuilder}; use strict_types::TypeLib; -use crate::library::LibSite; +use crate::library::{Lib, LibSite}; use crate::LIB_NAME_ALUVM; /// Strict type id for the library providing data types from this crate. pub const LIB_ID_ALUVM: &str = - "urn:ubideco:stl:DVtm25LRKU4TjbyZmVxPhvCmctZ6vKkPKqfpU2QsDNUo#exodus-axiom-tommy"; + "urn:ubideco:stl:APYERRUMyWqLadwTv8tEFifHMPGpL3xGFSBxwaKYpmcV#square-mammal-uncle"; fn _aluvm_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_ALUVM), tiny_bset! { + strict_types::stl::std_stl().to_dependency(), strict_types::stl::strict_types_stl().to_dependency() }) .transpile::() + .transpile::() .compile() } diff --git a/stl/AluVM@0.1.0.sta b/stl/AluVM@0.1.0.sta index 8dc829f..603e5a1 100644 --- a/stl/AluVM@0.1.0.sta +++ b/stl/AluVM@0.1.0.sta @@ -1,10 +1,19 @@ -----BEGIN STRICT TYPE LIB----- -Id: urn:ubideco:stl:DVtm25LRKU4TjbyZmVxPhvCmctZ6vKkPKqfpU2QsDNUo#exodus-axiom-tommy +Id: urn:ubideco:stl:APYERRUMyWqLadwTv8tEFifHMPGpL3xGFSBxwaKYpmcV#square-mammal-uncle Name: AluVM -Checksum-SHA256: 225116c3d4fca2a386113db3fd9ffcd2952f3a5e7ea4f1391dd0a6fc139e97ec +Dependency: EcCNgrgLaygt3tCZNu2ZVEzMzSAZYEUeTNAVi5E81YWi#aspirin-mango-average +Checksum-SHA256: d0b37000d730beef023af7883b4664c10ecce81ab144c4ac0cc4a85687b96fac -1wm|eR!sl^0ssX}X<|ua1pxpD002NB00&HIVpC~!Wd;HRY-wTvr!Z9lE%{u?@QI^ -EqCb}2Q7OO^w+`_q*ddTXmHSf)18{G10006 +1wm|eR!srQEFN!zncXl9K5w2;FV{y1jDTJCC^p$-mHEbO0#qjhQ*>kj15cBr>9x-Cs#sheE97?nsOaXHkb)PY;b5{Lt$`p1^@?1b74+lZDj +=k00;ugEFN!zncXl9K5w2;FV{y1jDTJCC^p$-mHEbO0#qj_gwc#^4#qsMUl{*1zN +e>I^CwqAYJB+ZKALhJOg5MT00000000080000000006NpoRSWoHEe00{wQLP%5x# ++Iwz1XmoMZUe8>*JiqdOAW_7Xq?evcVyE5000000000$0000000003Ole{U1O#bw +VPydUTVY%eB()|DVs5)j?WBU==a=KzyTg3px>h43uaSoYV{c?-00;m8KmY&$0000 +00RR600000000d-VbYTDp002M$0000000030{{R3000004Y-wV10n*|aTPJY^=0y +A22XT>