tensorfs 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. tensorfs-0.1.0/Cargo.lock +325 -0
  2. tensorfs-0.1.0/Cargo.toml +7 -0
  3. tensorfs-0.1.0/PKG-INFO +8 -0
  4. tensorfs-0.1.0/crates/tensorfs-core/Cargo.toml +21 -0
  5. tensorfs-0.1.0/crates/tensorfs-core/src/b64.rs +83 -0
  6. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/ckpt.rs +1114 -0
  7. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/conform.rs +708 -0
  8. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/fetch.rs +147 -0
  9. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/fixtures.rs +286 -0
  10. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/ingest.rs +2103 -0
  11. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/layout.rs +1066 -0
  12. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/main.rs +2899 -0
  13. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/rd.rs +1099 -0
  14. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/repo.rs +1041 -0
  15. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/snap.rs +399 -0
  16. tensorfs-0.1.0/crates/tensorfs-core/src/bin/tfs/watch.rs +228 -0
  17. tensorfs-0.1.0/crates/tensorfs-core/src/canon.rs +451 -0
  18. tensorfs-0.1.0/crates/tensorfs-core/src/capability.rs +282 -0
  19. tensorfs-0.1.0/crates/tensorfs-core/src/catalog.rs +1151 -0
  20. tensorfs-0.1.0/crates/tensorfs-core/src/cbor.rs +740 -0
  21. tensorfs-0.1.0/crates/tensorfs-core/src/checkpoint.rs +495 -0
  22. tensorfs-0.1.0/crates/tensorfs-core/src/compat.rs +1320 -0
  23. tensorfs-0.1.0/crates/tensorfs-core/src/corpus.rs +183 -0
  24. tensorfs-0.1.0/crates/tensorfs-core/src/derived.rs +2741 -0
  25. tensorfs-0.1.0/crates/tensorfs-core/src/dtype.rs +112 -0
  26. tensorfs-0.1.0/crates/tensorfs-core/src/err.rs +205 -0
  27. tensorfs-0.1.0/crates/tensorfs-core/src/fetch.rs +618 -0
  28. tensorfs-0.1.0/crates/tensorfs-core/src/fill.rs +269 -0
  29. tensorfs-0.1.0/crates/tensorfs-core/src/grade.rs +950 -0
  30. tensorfs-0.1.0/crates/tensorfs-core/src/header.rs +1078 -0
  31. tensorfs-0.1.0/crates/tensorfs-core/src/ids.rs +221 -0
  32. tensorfs-0.1.0/crates/tensorfs-core/src/ingest/carrier.rs +738 -0
  33. tensorfs-0.1.0/crates/tensorfs-core/src/ingest/convert.rs +1700 -0
  34. tensorfs-0.1.0/crates/tensorfs-core/src/ingest/fingerprint.rs +1059 -0
  35. tensorfs-0.1.0/crates/tensorfs-core/src/ingest/gguf.rs +599 -0
  36. tensorfs-0.1.0/crates/tensorfs-core/src/ingest/mod.rs +404 -0
  37. tensorfs-0.1.0/crates/tensorfs-core/src/ingest/source.rs +1491 -0
  38. tensorfs-0.1.0/crates/tensorfs-core/src/ingest/stamp.rs +430 -0
  39. tensorfs-0.1.0/crates/tensorfs-core/src/ingest/transaction.rs +728 -0
  40. tensorfs-0.1.0/crates/tensorfs-core/src/lib.rs +41 -0
  41. tensorfs-0.1.0/crates/tensorfs-core/src/limits.rs +62 -0
  42. tensorfs-0.1.0/crates/tensorfs-core/src/machine.rs +511 -0
  43. tensorfs-0.1.0/crates/tensorfs-core/src/manifest.rs +258 -0
  44. tensorfs-0.1.0/crates/tensorfs-core/src/meta.rs +278 -0
  45. tensorfs-0.1.0/crates/tensorfs-core/src/project.rs +525 -0
  46. tensorfs-0.1.0/crates/tensorfs-core/src/read.rs +1032 -0
  47. tensorfs-0.1.0/crates/tensorfs-core/src/receipt.rs +90 -0
  48. tensorfs-0.1.0/crates/tensorfs-core/src/registry.rs +404 -0
  49. tensorfs-0.1.0/crates/tensorfs-core/src/relation.rs +366 -0
  50. tensorfs-0.1.0/crates/tensorfs-core/src/repository.rs +1504 -0
  51. tensorfs-0.1.0/crates/tensorfs-core/src/sha256.rs +135 -0
  52. tensorfs-0.1.0/crates/tensorfs-core/src/spec.rs +203 -0
  53. tensorfs-0.1.0/crates/tensorfs-core/src/storage.rs +1750 -0
  54. tensorfs-0.1.0/crates/tensorfs-core/src/store.rs +1315 -0
  55. tensorfs-0.1.0/crates/tensorfs-core/src/structure.rs +282 -0
  56. tensorfs-0.1.0/crates/tensorfs-core/src/vectors.rs +218 -0
  57. tensorfs-0.1.0/crates/tensorfs-core/src/verifier.rs +256 -0
  58. tensorfs-0.1.0/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +1 -0
  59. tensorfs-0.1.0/crates/tensorfs-core/tests/fixtures/repo_apply/evidence.json +1 -0
  60. tensorfs-0.1.0/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +1 -0
  61. tensorfs-0.1.0/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +1 -0
  62. tensorfs-0.1.0/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +3 -0
  63. tensorfs-0.1.0/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
  64. tensorfs-0.1.0/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +1 -0
  65. tensorfs-0.1.0/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +1 -0
  66. tensorfs-0.1.0/crates/tensorfs-core/tests/grade.rs +1023 -0
  67. tensorfs-0.1.0/crates/tensorfs-core/tests/liveness.rs +344 -0
  68. tensorfs-0.1.0/crates/tensorfs-core/tests/repo_store.rs +1149 -0
  69. tensorfs-0.1.0/crates/tensorfs-core/tests/source_plan.rs +571 -0
  70. tensorfs-0.1.0/crates/tensorfs-core/tests/topology_projection.rs +383 -0
  71. tensorfs-0.1.0/crates/tensorfs-py/Cargo.toml +25 -0
  72. tensorfs-0.1.0/crates/tensorfs-py/src/docs.rs +362 -0
  73. tensorfs-0.1.0/crates/tensorfs-py/src/errors.rs +119 -0
  74. tensorfs-0.1.0/crates/tensorfs-py/src/lib.rs +106 -0
  75. tensorfs-0.1.0/crates/tensorfs-py/src/store.rs +2103 -0
  76. tensorfs-0.1.0/pyproject.toml +20 -0
  77. tensorfs-0.1.0/python/tensorfs/__init__.py +134 -0
  78. tensorfs-0.1.0/python/tensorfs/errors.py +31 -0
@@ -0,0 +1,325 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "ahash"
7
+ version = "0.8.12"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
10
+ dependencies = [
11
+ "cfg-if",
12
+ "once_cell",
13
+ "version_check",
14
+ "zerocopy",
15
+ ]
16
+
17
+ [[package]]
18
+ name = "bitflags"
19
+ version = "2.13.1"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
22
+
23
+ [[package]]
24
+ name = "cc"
25
+ version = "1.4.4"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273"
28
+ dependencies = [
29
+ "find-msvc-tools",
30
+ "shlex",
31
+ ]
32
+
33
+ [[package]]
34
+ name = "cfg-if"
35
+ version = "1.0.4"
36
+ source = "registry+https://github.com/rust-lang/crates.io-index"
37
+ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
38
+
39
+ [[package]]
40
+ name = "fallible-iterator"
41
+ version = "0.3.0"
42
+ source = "registry+https://github.com/rust-lang/crates.io-index"
43
+ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
44
+
45
+ [[package]]
46
+ name = "fallible-streaming-iterator"
47
+ version = "0.1.9"
48
+ source = "registry+https://github.com/rust-lang/crates.io-index"
49
+ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
50
+
51
+ [[package]]
52
+ name = "find-msvc-tools"
53
+ version = "0.1.11"
54
+ source = "registry+https://github.com/rust-lang/crates.io-index"
55
+ checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
56
+
57
+ [[package]]
58
+ name = "fs2"
59
+ version = "0.4.3"
60
+ source = "registry+https://github.com/rust-lang/crates.io-index"
61
+ checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
62
+ dependencies = [
63
+ "libc",
64
+ "winapi",
65
+ ]
66
+
67
+ [[package]]
68
+ name = "hashbrown"
69
+ version = "0.14.5"
70
+ source = "registry+https://github.com/rust-lang/crates.io-index"
71
+ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
72
+ dependencies = [
73
+ "ahash",
74
+ ]
75
+
76
+ [[package]]
77
+ name = "hashlink"
78
+ version = "0.9.1"
79
+ source = "registry+https://github.com/rust-lang/crates.io-index"
80
+ checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
81
+ dependencies = [
82
+ "hashbrown",
83
+ ]
84
+
85
+ [[package]]
86
+ name = "heck"
87
+ version = "0.5.0"
88
+ source = "registry+https://github.com/rust-lang/crates.io-index"
89
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
90
+
91
+ [[package]]
92
+ name = "libc"
93
+ version = "0.2.189"
94
+ source = "registry+https://github.com/rust-lang/crates.io-index"
95
+ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
96
+
97
+ [[package]]
98
+ name = "libsqlite3-sys"
99
+ version = "0.30.1"
100
+ source = "registry+https://github.com/rust-lang/crates.io-index"
101
+ checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
102
+ dependencies = [
103
+ "cc",
104
+ "pkg-config",
105
+ "vcpkg",
106
+ ]
107
+
108
+ [[package]]
109
+ name = "minicbor"
110
+ version = "2.3.0"
111
+ source = "registry+https://github.com/rust-lang/crates.io-index"
112
+ checksum = "c12b4033ffaa92fbf9df03df38d19324f52bad130dd223f811734a8006dd2d69"
113
+
114
+ [[package]]
115
+ name = "once_cell"
116
+ version = "1.21.4"
117
+ source = "registry+https://github.com/rust-lang/crates.io-index"
118
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
119
+
120
+ [[package]]
121
+ name = "pkg-config"
122
+ version = "0.3.34"
123
+ source = "registry+https://github.com/rust-lang/crates.io-index"
124
+ checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
125
+
126
+ [[package]]
127
+ name = "portable-atomic"
128
+ version = "1.15.0"
129
+ source = "registry+https://github.com/rust-lang/crates.io-index"
130
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
131
+
132
+ [[package]]
133
+ name = "proc-macro2"
134
+ version = "1.0.107"
135
+ source = "registry+https://github.com/rust-lang/crates.io-index"
136
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
137
+ dependencies = [
138
+ "unicode-ident",
139
+ ]
140
+
141
+ [[package]]
142
+ name = "pyo3"
143
+ version = "0.29.2"
144
+ source = "registry+https://github.com/rust-lang/crates.io-index"
145
+ checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
146
+ dependencies = [
147
+ "libc",
148
+ "once_cell",
149
+ "portable-atomic",
150
+ "pyo3-build-config",
151
+ "pyo3-ffi",
152
+ "pyo3-macros",
153
+ ]
154
+
155
+ [[package]]
156
+ name = "pyo3-build-config"
157
+ version = "0.29.2"
158
+ source = "registry+https://github.com/rust-lang/crates.io-index"
159
+ checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
160
+ dependencies = [
161
+ "target-lexicon",
162
+ ]
163
+
164
+ [[package]]
165
+ name = "pyo3-ffi"
166
+ version = "0.29.2"
167
+ source = "registry+https://github.com/rust-lang/crates.io-index"
168
+ checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
169
+ dependencies = [
170
+ "libc",
171
+ "pyo3-build-config",
172
+ ]
173
+
174
+ [[package]]
175
+ name = "pyo3-macros"
176
+ version = "0.29.2"
177
+ source = "registry+https://github.com/rust-lang/crates.io-index"
178
+ checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
179
+ dependencies = [
180
+ "proc-macro2",
181
+ "pyo3-macros-backend",
182
+ "quote",
183
+ "syn",
184
+ ]
185
+
186
+ [[package]]
187
+ name = "pyo3-macros-backend"
188
+ version = "0.29.2"
189
+ source = "registry+https://github.com/rust-lang/crates.io-index"
190
+ checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
191
+ dependencies = [
192
+ "heck",
193
+ "proc-macro2",
194
+ "quote",
195
+ "syn",
196
+ ]
197
+
198
+ [[package]]
199
+ name = "quote"
200
+ version = "1.0.47"
201
+ source = "registry+https://github.com/rust-lang/crates.io-index"
202
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
203
+ dependencies = [
204
+ "proc-macro2",
205
+ ]
206
+
207
+ [[package]]
208
+ name = "rusqlite"
209
+ version = "0.32.1"
210
+ source = "registry+https://github.com/rust-lang/crates.io-index"
211
+ checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
212
+ dependencies = [
213
+ "bitflags",
214
+ "fallible-iterator",
215
+ "fallible-streaming-iterator",
216
+ "hashlink",
217
+ "libsqlite3-sys",
218
+ "smallvec",
219
+ ]
220
+
221
+ [[package]]
222
+ name = "shlex"
223
+ version = "2.0.1"
224
+ source = "registry+https://github.com/rust-lang/crates.io-index"
225
+ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
226
+
227
+ [[package]]
228
+ name = "smallvec"
229
+ version = "1.15.2"
230
+ source = "registry+https://github.com/rust-lang/crates.io-index"
231
+ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
232
+
233
+ [[package]]
234
+ name = "syn"
235
+ version = "2.0.119"
236
+ source = "registry+https://github.com/rust-lang/crates.io-index"
237
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
238
+ dependencies = [
239
+ "proc-macro2",
240
+ "quote",
241
+ "unicode-ident",
242
+ ]
243
+
244
+ [[package]]
245
+ name = "target-lexicon"
246
+ version = "0.13.5"
247
+ source = "registry+https://github.com/rust-lang/crates.io-index"
248
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
249
+
250
+ [[package]]
251
+ name = "tensorfs-core"
252
+ version = "0.1.0"
253
+ dependencies = [
254
+ "fs2",
255
+ "minicbor",
256
+ "rusqlite",
257
+ ]
258
+
259
+ [[package]]
260
+ name = "tensorfs-py"
261
+ version = "0.1.0"
262
+ dependencies = [
263
+ "pyo3",
264
+ "tensorfs-core",
265
+ ]
266
+
267
+ [[package]]
268
+ name = "unicode-ident"
269
+ version = "1.0.24"
270
+ source = "registry+https://github.com/rust-lang/crates.io-index"
271
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
272
+
273
+ [[package]]
274
+ name = "vcpkg"
275
+ version = "0.2.15"
276
+ source = "registry+https://github.com/rust-lang/crates.io-index"
277
+ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
278
+
279
+ [[package]]
280
+ name = "version_check"
281
+ version = "0.9.5"
282
+ source = "registry+https://github.com/rust-lang/crates.io-index"
283
+ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
284
+
285
+ [[package]]
286
+ name = "winapi"
287
+ version = "0.3.9"
288
+ source = "registry+https://github.com/rust-lang/crates.io-index"
289
+ checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
290
+ dependencies = [
291
+ "winapi-i686-pc-windows-gnu",
292
+ "winapi-x86_64-pc-windows-gnu",
293
+ ]
294
+
295
+ [[package]]
296
+ name = "winapi-i686-pc-windows-gnu"
297
+ version = "0.4.0"
298
+ source = "registry+https://github.com/rust-lang/crates.io-index"
299
+ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
300
+
301
+ [[package]]
302
+ name = "winapi-x86_64-pc-windows-gnu"
303
+ version = "0.4.0"
304
+ source = "registry+https://github.com/rust-lang/crates.io-index"
305
+ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
306
+
307
+ [[package]]
308
+ name = "zerocopy"
309
+ version = "0.8.56"
310
+ source = "registry+https://github.com/rust-lang/crates.io-index"
311
+ checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
312
+ dependencies = [
313
+ "zerocopy-derive",
314
+ ]
315
+
316
+ [[package]]
317
+ name = "zerocopy-derive"
318
+ version = "0.8.56"
319
+ source = "registry+https://github.com/rust-lang/crates.io-index"
320
+ checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
321
+ dependencies = [
322
+ "proc-macro2",
323
+ "quote",
324
+ "syn",
325
+ ]
@@ -0,0 +1,7 @@
1
+ [workspace]
2
+ resolver = "2"
3
+ members = ["crates/tensorfs-core", "crates/tensorfs-py"]
4
+
5
+ [workspace.package]
6
+ version = "0.1.0"
7
+ edition = "2021"
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: tensorfs
3
+ Version: 0.1.0
4
+ Classifier: Programming Language :: Python :: 3
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Rust
7
+ Summary: TensorFS — canonical tensor artifact substrate
8
+ Requires-Python: >=3.12
@@ -0,0 +1,21 @@
1
+ [package]
2
+ name = "tensorfs-core"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+
6
+ [features]
7
+ default = ["grade"]
8
+ # Hub-side catalog grading (grade.rs, verifier.rs). The worker wheel builds WITHOUT it
9
+ # (tensorfs-py sets default-features = false), so catalog-grading vocabulary stays out of
10
+ # the serving path; the tfs CLI requires it.
11
+ grade = []
12
+
13
+ [[bin]]
14
+ name = "tfs"
15
+ path = "src/bin/tfs/main.rs"
16
+ required-features = ["grade"]
17
+
18
+ [dependencies]
19
+ fs2 = "0.4"
20
+ minicbor = { version = "2.3.0", default-features = false, features = ["alloc"] }
21
+ rusqlite = { version = "0.32", features = ["bundled"] }
@@ -0,0 +1,83 @@
1
+ //! Canonical padded RFC 4648 base64 with decode->re-encode identity.
2
+
3
+ use crate::err::{refuse, Code, Result};
4
+
5
+ const A: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6
+
7
+ pub fn encode(data: &[u8]) -> String {
8
+ let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
9
+ for c in data.chunks(3) {
10
+ let b = [c[0], *c.get(1).unwrap_or(&0), *c.get(2).unwrap_or(&0)];
11
+ let n = ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | b[2] as u32;
12
+ out.push(A[(n >> 18) as usize & 63] as char);
13
+ out.push(A[(n >> 12) as usize & 63] as char);
14
+ out.push(if c.len() > 1 {
15
+ A[(n >> 6) as usize & 63] as char
16
+ } else {
17
+ '='
18
+ });
19
+ out.push(if c.len() > 2 {
20
+ A[n as usize & 63] as char
21
+ } else {
22
+ '='
23
+ });
24
+ }
25
+ out
26
+ }
27
+
28
+ /// Strict decode: padded, no whitespace, canonical (re-encode must reproduce the input).
29
+ pub fn decode(s: &str) -> Result<Vec<u8>> {
30
+ let b = s.as_bytes();
31
+ if !b.len().is_multiple_of(4) {
32
+ return refuse(
33
+ Code::BAD_BASE64,
34
+ format!("length {} not a multiple of 4", b.len()),
35
+ );
36
+ }
37
+ let val = |c: u8| -> Option<u32> {
38
+ match c {
39
+ b'A'..=b'Z' => Some((c - b'A') as u32),
40
+ b'a'..=b'z' => Some((c - b'a') as u32 + 26),
41
+ b'0'..=b'9' => Some((c - b'0') as u32 + 52),
42
+ b'+' => Some(62),
43
+ b'/' => Some(63),
44
+ _ => None,
45
+ }
46
+ };
47
+ let mut out = Vec::with_capacity(b.len() / 4 * 3);
48
+ for (i, q) in b.chunks(4).enumerate() {
49
+ let last = i == b.len() / 4 - 1;
50
+ let pad = q.iter().filter(|c| **c == b'=').count();
51
+ if pad > 2 || (pad > 0 && !last) {
52
+ return refuse(Code::BAD_BASE64, "misplaced padding");
53
+ }
54
+ let mut n = 0u32;
55
+ for (j, c) in q.iter().enumerate() {
56
+ let v = if *c == b'=' {
57
+ if j < 4 - pad {
58
+ return refuse(Code::BAD_BASE64, "padding inside a quantum");
59
+ }
60
+ 0
61
+ } else {
62
+ match val(*c) {
63
+ Some(v) => v,
64
+ None => {
65
+ return refuse(Code::BAD_BASE64, format!("illegal byte {:?}", *c as char))
66
+ }
67
+ }
68
+ };
69
+ n = (n << 6) | v;
70
+ }
71
+ out.push((n >> 16) as u8);
72
+ if pad < 2 {
73
+ out.push((n >> 8) as u8);
74
+ }
75
+ if pad < 1 {
76
+ out.push(n as u8);
77
+ }
78
+ }
79
+ if encode(&out) != s {
80
+ return refuse(Code::BAD_BASE64, "non-canonical base64 (re-encode differs)");
81
+ }
82
+ Ok(out)
83
+ }