• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacks-network / stacks-core / 26599399731-1

28 May 2026 08:09PM UTC coverage: 85.963% (+0.004%) from 85.959%
26599399731-1

Pull #7248

github

eb8995
web-flow
Merge e128b5be2 into 1d11876a5
Pull Request #7248: Chore: merge develop to pox-wf-integration

5431 of 5984 new or added lines in 42 files covered. (90.76%)

4909 existing lines in 78 files now uncovered.

194022 of 225705 relevant lines covered (85.96%)

18479886.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.91
/stacks-codec/src/strings.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020-2026 Stacks Open Internet Foundation
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
use std::fmt;
18
use std::io::{Read, Write};
19
use std::ops::{Deref, DerefMut};
20

21
use clarity_types::representations::{ClarityName, ContractName};
22
use serde::{Deserialize, Serialize};
23
use stacks_common::codec::{
24
    read_next, write_next, Error as codec_error, StacksMessageCodec, MAX_MESSAGE_LEN,
25
};
26
use stacks_common::util::retry::BoundReader;
27

28
/// printable-ASCII-only string, but encodable.
29
/// Note that it cannot be longer than ARRAY_MAX_LEN (4.1 billion bytes)
30
#[derive(Clone, PartialEq, Serialize, Deserialize)]
31
pub struct StacksString(Vec<u8>);
32

33
impl fmt::Display for StacksString {
NEW
34
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
NEW
35
        f.write_str(String::from_utf8_lossy(self).into_owned().as_str())
×
NEW
36
    }
×
37
}
38

39
impl fmt::Debug for StacksString {
40
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81,510✔
41
        f.write_str(String::from_utf8_lossy(self).into_owned().as_str())
81,510✔
42
    }
81,510✔
43
}
44

45
impl Deref for StacksString {
46
    type Target = Vec<u8>;
47
    fn deref(&self) -> &Vec<u8> {
81,520✔
48
        &self.0
81,520✔
49
    }
81,520✔
50
}
51

52
impl DerefMut for StacksString {
NEW
53
    fn deref_mut(&mut self) -> &mut Vec<u8> {
×
NEW
54
        &mut self.0
×
NEW
55
    }
×
56
}
57

58
impl StacksMessageCodec for StacksString {
59
    fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), codec_error> {
4,414,962✔
60
        write_next(fd, &self.0)
4,414,962✔
61
    }
4,414,962✔
62

63
    fn consensus_deserialize<R: Read>(fd: &mut R) -> Result<StacksString, codec_error> {
409,049✔
64
        let bytes: Vec<u8> = {
407,267✔
65
            let mut bound_read = BoundReader::from_reader(fd, MAX_MESSAGE_LEN as u64);
409,049✔
66
            read_next(&mut bound_read)
409,049✔
67
        }?;
1,782✔
68

69
        // must encode a valid string
70
        let s = String::from_utf8(bytes.clone()).map_err(|_e| {
407,267✔
NEW
71
            codec_error::DeserializeError(
×
NEW
72
                "Invalid Stacks string: could not build from utf8".to_string(),
×
NEW
73
            )
×
NEW
74
        })?;
×
75

76
        if !StacksString::is_valid_string(&s) {
407,267✔
77
            // non-printable ASCII or not ASCII
NEW
78
            return Err(codec_error::DeserializeError(
×
NEW
79
                "Invalid Stacks string: non-printable or non-ASCII string".to_string(),
×
NEW
80
            ));
×
81
        }
407,267✔
82

83
        Ok(StacksString(bytes))
407,267✔
84
    }
409,049✔
85
}
86

87
impl From<ClarityName> for StacksString {
88
    fn from(clarity_name: ClarityName) -> StacksString {
615,930✔
89
        // .unwrap() is safe since StacksString is less strict
90
        StacksString::from_str(&clarity_name).unwrap()
615,930✔
91
    }
615,930✔
92
}
93

94
impl From<ContractName> for StacksString {
NEW
95
    fn from(contract_name: ContractName) -> StacksString {
×
96
        // .unwrap() is safe since StacksString is less strict
NEW
97
        StacksString::from_str(&contract_name).unwrap()
×
NEW
98
    }
×
99
}
100

101
impl StacksString {
102
    /// Is the given string a valid Clarity string?
103
    pub fn is_valid_string(s: &String) -> bool {
2,636,980✔
104
        s.is_ascii() && StacksString::is_printable(s)
2,636,980✔
105
    }
2,636,980✔
106

107
    pub fn is_printable(s: &String) -> bool {
2,636,980✔
108
        if !s.is_ascii() {
2,636,980✔
NEW
109
            return false;
×
110
        }
2,636,980✔
111
        // all characters must be ASCII "printable" characters, excluding "delete".
112
        // This is 0x20 through 0x7e, inclusive, as well as '\t' and '\n'
113
        // TODO: DRY up with vm::representations
114
        for c in s.as_bytes().iter() {
2,147,483,647✔
115
            if (*c < 0x20 && *c != b'\t' && *c != b'\n') || *c > 0x7e {
2,147,483,647✔
116
                return false;
20✔
117
            }
2,147,483,647✔
118
        }
119
        true
2,636,960✔
120
    }
2,636,980✔
121

122
    pub fn is_clarity_variable(&self) -> bool {
615,930✔
123
        ClarityName::try_from(self.to_string()).is_ok()
615,930✔
124
    }
615,930✔
125

126
    pub fn from_string(s: &String) -> Option<StacksString> {
32,290✔
127
        if !StacksString::is_valid_string(s) {
32,290✔
NEW
128
            return None;
×
129
        }
32,290✔
130
        Some(StacksString(s.as_bytes().to_vec()))
32,290✔
131
    }
32,290✔
132

133
    pub fn from_str(s: &str) -> Option<StacksString> {
1,306,500✔
134
        if !StacksString::is_valid_string(&String::from(s)) {
1,306,500✔
135
            return None;
20✔
136
        }
1,306,480✔
137
        Some(StacksString(s.as_bytes().to_vec()))
1,306,480✔
138
    }
1,306,500✔
139

140
    pub fn to_string(&self) -> String {
1,405,480✔
141
        // guaranteed to always succeed because the string is ASCII
142
        String::from_utf8(self.0.clone()).unwrap()
1,405,480✔
143
    }
1,405,480✔
144
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc