Skip to content

Commit 29e755d

Browse files
authored
Merge pull request #355 from Dstack-TEE/size-parser
Create crate size-parser
2 parents 25fc3fc + 7aedbde commit 29e755d

File tree

7 files changed

+819
-30
lines changed

7 files changed

+819
-30
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ members = [
5151
"dstack-mr/cli",
5252
"verifier",
5353
"no_std_check",
54+
"size-parser",
5455
]
5556
resolver = "2"
5657

@@ -81,6 +82,7 @@ lspci = { path = "lspci" }
8182
sodiumbox = { path = "sodiumbox" }
8283
serde-duration = { path = "serde-duration" }
8384
dstack-mr = { path = "dstack-mr" }
85+
size-parser = { path = "size-parser" }
8486

8587
# Core dependencies
8688
anyhow = { version = "1.0.97", default-features = false }

dstack-mr/cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ dstack-types.workspace = true
2121
fs-err.workspace = true
2222
serde_json = { workspace = true, features = ["alloc"] }
2323
tracing-subscriber.workspace = true
24+
size-parser.workspace = true

dstack-mr/cli/src/main.rs

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44

5-
use anyhow::{Context, Result, anyhow};
5+
use anyhow::{Context, Result};
66
use clap::{Parser, Subcommand};
77
use dstack_mr::Machine;
88
use dstack_types::ImageInfo;
99
use fs_err as fs;
10+
use size_parser::parse_memory_size;
1011
use std::path::PathBuf;
1112

1213
#[derive(Parser)]
@@ -130,32 +131,3 @@ fn main() -> Result<()> {
130131

131132
Ok(())
132133
}
133-
134-
/// Parse a memory size value that can be decimal or hexadecimal (with 0x prefix)
135-
fn parse_memory_size(s: &str) -> Result<u64> {
136-
let s = s.trim();
137-
138-
if s.is_empty() {
139-
return Err(anyhow!("Empty memory size"));
140-
}
141-
if s.starts_with("0x") || s.starts_with("0X") {
142-
let hex_str = &s[2..];
143-
return u64::from_str_radix(hex_str, 16)
144-
.map_err(|e| anyhow!("Invalid hexadecimal value: {}", e));
145-
}
146-
147-
if s.chars().all(|c| c.is_ascii_digit()) {
148-
return Ok(s.parse::<u64>()?);
149-
}
150-
let len = s.len();
151-
let (num_part, suffix) = match s.chars().last().unwrap() {
152-
'k' | 'K' => (&s[0..len - 1], 1024u64),
153-
'm' | 'M' => (&s[0..len - 1], 1024u64 * 1024),
154-
'g' | 'G' => (&s[0..len - 1], 1024u64 * 1024 * 1024),
155-
't' | 'T' => (&s[0..len - 1], 1024u64 * 1024 * 1024 * 1024),
156-
_ => return Err(anyhow!("Unknown memory size suffix")),
157-
};
158-
159-
let num = num_part.parse::<u64>()?;
160-
Ok(num * suffix)
161-
}

size-parser/Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-FileCopyrightText: © 2025 Phala Network <[email protected]>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
[package]
6+
name = "size-parser"
7+
version.workspace = true
8+
authors.workspace = true
9+
edition.workspace = true
10+
license.workspace = true
11+
homepage.workspace = true
12+
repository.workspace = true
13+
description = "A utility crate for parsing and handling memory sizes with serde support"
14+
15+
[dependencies]
16+
anyhow.workspace = true
17+
serde = { workspace = true, features = ["derive"], optional = true }
18+
thiserror.workspace = true
19+
20+
[features]
21+
default = []
22+
serde = ["dep:serde"]
23+
24+
[dev-dependencies]
25+
serde_json = { workspace = true, features = ["std"] }

size-parser/README.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# size-parser
2+
3+
A utility crate for parsing and handling memory sizes with serde support.
4+
5+
## Features
6+
7+
- Parse memory size strings with various suffixes (K, M, G, T)
8+
- Support for hexadecimal values (0x prefix)
9+
- Optional serde serialization/deserialization support
10+
- Human-readable formatting
11+
- Type-safe memory size handling
12+
13+
## Usage
14+
15+
Add this to your `Cargo.toml`:
16+
17+
```toml
18+
[dependencies]
19+
size-parser = { path = "../size-parser" }
20+
21+
# For serde support
22+
size-parser = { path = "../size-parser", features = ["serde"] }
23+
```
24+
25+
## Examples
26+
27+
### Basic Usage
28+
29+
```rust
30+
use size_parser::MemorySize;
31+
32+
// Parse from string
33+
let size = MemorySize::parse("2G").unwrap();
34+
assert_eq!(size.bytes(), 2 * 1024 * 1024 * 1024);
35+
36+
// Parse hexadecimal
37+
let size = MemorySize::parse("0x1000").unwrap();
38+
assert_eq!(size.bytes(), 4096);
39+
40+
// Create from bytes
41+
let size = MemorySize::from_bytes(1024);
42+
assert_eq!(size.bytes(), 1024);
43+
44+
// Using FromStr trait
45+
let size: MemorySize = "2G".parse().unwrap();
46+
assert_eq!(size.bytes(), 2 * 1024 * 1024 * 1024);
47+
```
48+
49+
### Supported Formats
50+
51+
- Plain numbers: `"1024"`, `"2048"`
52+
- Hexadecimal: `"0x1000"`, `"0X2000"`
53+
- With suffixes: `"2K"`, `"4M"`, `"1G"`, `"2T"` (case-insensitive)
54+
55+
Suffixes use binary (1024-based) multipliers:
56+
- K/k: 1024 bytes
57+
- M/m: 1024² bytes
58+
- G/g: 1024³ bytes
59+
- T/t: 1024⁴ bytes
60+
61+
### Human-readable Formatting
62+
63+
```rust
64+
let size = MemorySize::from_bytes(1536);
65+
println!("{}", size); // Prints: "1.5K"
66+
67+
let size = MemorySize::from_bytes(2 * 1024 * 1024 * 1024);
68+
println!("{}", size); // Prints: "2G"
69+
```
70+
71+
### Serde Support (with "serde" feature)
72+
73+
#### Using MemorySize Type
74+
75+
```rust
76+
use size_parser::MemorySize;
77+
use serde::{Deserialize, Serialize};
78+
79+
#[derive(Serialize, Deserialize)]
80+
struct Config {
81+
memory: MemorySize,
82+
}
83+
84+
let config = Config {
85+
memory: MemorySize::parse("2G").unwrap(),
86+
};
87+
88+
// Serializes as: {"memory": "2G"}
89+
let json = serde_json::to_string(&config).unwrap();
90+
91+
// Can deserialize from various formats
92+
let config: Config = serde_json::from_str(r#"{"memory": "1024M"}"#).unwrap();
93+
```
94+
95+
#### Using Field Attributes with Numeric Types
96+
97+
You can also use serde field attributes to serialize/deserialize memory sizes directly into any numeric type that can be converted to/from u64:
98+
99+
```rust
100+
use serde::{Deserialize, Serialize};
101+
102+
#[derive(Serialize, Deserialize)]
103+
struct MyConfig {
104+
#[serde(with = "size_parser::human_size")]
105+
memory_size: u64,
106+
#[serde(with = "size_parser::human_size")]
107+
buffer_size: usize,
108+
#[serde(with = "size_parser::human_size")]
109+
cache_size: u32,
110+
}
111+
112+
let config = MyConfig {
113+
memory_size: 2 * 1024 * 1024 * 1024, // 2GB
114+
buffer_size: 512 * 1024, // 512KB
115+
cache_size: 64 * 1024, // 64KB
116+
};
117+
118+
// Serializes as: {"memory_size": "2G", "buffer_size": "512K", "cache_size": "64K"}
119+
let json = serde_json::to_string(&config).unwrap();
120+
121+
// Can deserialize from human-readable formats
122+
let config: MyConfig = serde_json::from_str(r#"{"memory_size": "1G", "buffer_size": "256K", "cache_size": "32K"}"#).unwrap();
123+
assert_eq!(config.memory_size, 1024 * 1024 * 1024);
124+
assert_eq!(config.buffer_size, 256 * 1024);
125+
assert_eq!(config.cache_size, 32 * 1024);
126+
```
127+
128+
**Supported numeric types:**
129+
- `u64`, `u32`, `u16`, `u8` - unsigned integers
130+
- `usize` - platform-dependent unsigned integer
131+
- Any type that implements `TryFrom<u64>` and `Into<u64>`
132+
133+
The generic implementation automatically handles overflow checking and provides clear error messages when values are too large for the target type.
134+
135+
### Compatibility Function
136+
137+
For compatibility with existing code, a standalone function is also provided:
138+
139+
```rust
140+
use size_parser::parse_memory_size;
141+
142+
let bytes = parse_memory_size("2G").unwrap();
143+
assert_eq!(bytes, 2 * 1024 * 1024 * 1024);
144+
```
145+
146+
## License
147+
148+
Apache-2.0

0 commit comments

Comments
 (0)