Compare commits

...
Author SHA1 Message Date
Dylan M. Taylor 91b14c83cf Use shared Base64 for file and control data 2026-11-15 15:39:16 +00:00
5 changed files with 36 additions and 36 deletions
Generated
+16
View File
@@ -700,6 +700,7 @@ dependencies = [
"cadcraft-render",
"craft-docpath",
"craft-dxf",
"craft-mime",
"libc",
"log",
"rstar",
@@ -1131,6 +1132,11 @@ dependencies = [
"libc",
]
[[package]]
name = "craft-codepage"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=451a390542fbde0519cbb8ade3a3325cf02d97be#451a390542fbde0519cbb8ade3a3325cf02d97be"
[[package]]
name = "craft-docpath"
version = "0.1.0"
@@ -1183,6 +1189,16 @@ dependencies = [
"writeable",
]
[[package]]
name = "craft-mime"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=451a390542fbde0519cbb8ade3a3325cf02d97be#451a390542fbde0519cbb8ade3a3325cf02d97be"
dependencies = [
"chrono",
"craft-codepage",
"encoding_rs",
]
[[package]]
name = "craft-pdf"
version = "0.1.0"
+1
View File
@@ -16,6 +16,7 @@ cadcraft-geom = { path = "crates/geom" }
# DXF tags: the shared reader and writer (moved from crates/dxf; craft-libs#791).
craft-dxf = { git = "https://github.com/storytold/craft-libs", rev = "006e4df225fca57e95ead0fb1f91920900798a99" }
craft-dwg = { git = "https://github.com/storytold/craft-libs", rev = "7bb6c90076a0177d01cc4c482e7fd8dc9f31a8bf" }
craft-mime = { git = "https://github.com/storytold/craft-libs", rev = "451a390542fbde0519cbb8ade3a3325cf02d97be" }
cadcraft-color = { path = "crates/color" }
cadcraft-doc = { path = "crates/doc" }
cadcraft-constraints = { path = "crates/constraints" }
+1
View File
@@ -12,6 +12,7 @@ workspace = true
cadcraft-geom = { workspace = true }
cadcraft-color = { workspace = true }
cadcraft-doc = { workspace = true }
craft-mime = { workspace = true }
craft-dxf = { workspace = true }
cadcraft-constraints = { workspace = true }
cadcraft-fonts = { workspace = true }
+4 -36
View File
@@ -1020,46 +1020,14 @@ fn run_saveas_reporting(s: &mut Session, p: &Value) -> Result<Value> {
save_reporting(s, p, run_saveas, asked)
}
const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/// Encode bytes with the standard padded Base64 alphabet.
pub fn base64_encode(data: &[u8]) -> String {
let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
for chunk in data.chunks(3) {
let b = [chunk.first().copied().unwrap_or(0), chunk.get(1).copied().unwrap_or(0), chunk.get(2).copied().unwrap_or(0)];
let n = (u32::from(b[0]) << 16) | (u32::from(b[1]) << 8) | u32::from(b[2]);
for i in 0..4 {
if i <= chunk.len() {
out.push(char::from(B64[((n >> (18 - 6 * i)) & 63) as usize]));
} else {
out.push('=');
}
}
}
out
craft_mime::base64::STANDARD.encode(data)
}
/// Decode the legacy file/control Base64 form, retaining its permissive alphabet and padding.
pub fn base64_decode(s: &str) -> Option<Vec<u8>> {
let mut out = Vec::with_capacity(s.len() * 3 / 4);
let mut buf = 0u32;
let mut bits = 0;
for c in s.bytes() {
let v = match c {
b'A'..=b'Z' => c - b'A',
b'a'..=b'z' => c - b'a' + 26,
b'0'..=b'9' => c - b'0' + 52,
b'+' | b'-' => 62,
b'/' | b'_' => 63,
b'=' | b'\n' | b'\r' | b' ' => continue,
_ => return None,
};
buf = (buf << 6) | u32::from(v);
bits += 6;
if bits >= 8 {
bits -= 8;
out.push(((buf >> bits) & 0xff) as u8);
}
}
Some(out)
craft_mime::base64_compat::decode(s).ok()
}
#[cfg(test)]
+14
View File
@@ -0,0 +1,14 @@
//! File and automation inputs retain CADCraft's accepted Base64 forms.
use cadcraft_engine::cmd::file::{base64_decode, base64_encode};
#[test]
fn legacy_inputs_and_standard_outputs_survive_the_shared_cutover() {
for bytes in [&b""[..], b"f", b"fo", b"foo", b"foobar", &[0, 255, 127]] {
assert_eq!(base64_decode(&base64_encode(bytes)).as_deref(), Some(bytes));
}
assert_eq!(base64_decode("+_8=").as_deref(), Some(&[251, 255][..]));
assert_eq!(base64_decode("Z=m 9\nv\r").as_deref(), Some(&b"foo"[..]));
assert_eq!(base64_decode("A").as_deref(), Some(&b""[..]));
assert!(base64_decode("Zm9v\t").is_none());
assert!(base64_decode("Zm9v!").is_none());
}