Skip to content

Commit 33ac2cf

Browse files
sylvestrecakebaker
authored andcommitted
base58: it wasn't working properly with long input
1 parent e59685d commit 33ac2cf

File tree

2 files changed

+55
-13
lines changed

2 files changed

+55
-13
lines changed

src/uucore/src/lib/features/encoding.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -266,36 +266,50 @@ impl SupportsFastDecodeAndEncode for Base58Wrapper {
266266
return Ok(());
267267
}
268268

269-
// Convert bytes to big integer
270-
let mut num: Vec<u32> = Vec::new();
269+
// Convert bytes to big integer (Vec<u32> in little-endian format)
270+
let mut num = Vec::with_capacity(input_trimmed.len().div_ceil(4) + 1);
271271
for &byte in input_trimmed {
272-
let mut carry = byte as u32;
272+
let mut carry = byte as u64;
273273
for n in &mut num {
274-
let tmp = (*n as u64) * 256 + carry as u64;
274+
let tmp = (*n as u64) * 256 + carry;
275275
*n = tmp as u32;
276-
carry = (tmp >> 32) as u32;
276+
carry = tmp >> 32;
277277
}
278278
if carry > 0 {
279-
num.push(carry);
279+
num.push(carry as u32);
280280
}
281281
}
282282

283283
// Convert to base58
284-
let mut result = Vec::new();
284+
let mut result = Vec::with_capacity((input_trimmed.len() * 138 / 100) + 1);
285285
let alphabet = self.alphabet();
286286

287-
while !num.is_empty() && num.iter().any(|&n| n != 0) {
287+
// Optimized check: stop when all elements are zero
288+
while !num.is_empty() {
289+
// Check if we're done (all zeros)
290+
let mut all_zero = true;
288291
let mut carry = 0u64;
292+
289293
for n in num.iter_mut().rev() {
290294
let tmp = carry * (1u64 << 32) + *n as u64;
291295
*n = (tmp / 58) as u32;
292296
carry = tmp % 58;
297+
if *n != 0 {
298+
all_zero = false;
299+
}
293300
}
301+
294302
result.push(alphabet[carry as usize]);
295303

296-
// Remove leading zeros
297-
while num.last() == Some(&0) && num.len() > 1 {
298-
num.pop();
304+
if all_zero {
305+
break;
306+
}
307+
308+
// Trim trailing zeros less frequently
309+
if num.len() > 1 && result.len() % 8 == 0 {
310+
while num.last() == Some(&0) && num.len() > 1 {
311+
num.pop();
312+
}
299313
}
300314
}
301315

@@ -305,15 +319,18 @@ impl SupportsFastDecodeAndEncode for Base58Wrapper {
305319
}
306320

307321
// Add result (reversed because we built it backwards)
308-
for byte in result.into_iter().rev() {
322+
for &byte in result.iter().rev() {
309323
output.push_back(byte);
310324
}
311325

312326
Ok(())
313327
}
314328

315329
fn unpadded_multiple(&self) -> usize {
316-
1 // Base58 doesn't use padding
330+
// Base58 must encode the entire input as one big integer, not in chunks
331+
// Use a very large value to effectively disable chunking, but avoid overflow
332+
// when multiplied by ENCODE_IN_CHUNKS_OF_SIZE_MULTIPLE (1024) in base_common
333+
usize::MAX / 2048
317334
}
318335

319336
fn valid_decoding_multiple(&self) -> usize {

tests/by-util/test_basenc.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,31 @@ fn test_base58_decode() {
211211
.stdout_only("Hello, World!");
212212
}
213213

214+
#[test]
215+
fn test_base58_large_file_no_chunking() {
216+
// Regression test: base58 must process entire input as one big integer,
217+
// not in 1024-byte chunks. This test ensures files >1024 bytes work correctly.
218+
let (at, mut ucmd) = at_and_ucmd!();
219+
let filename = "large_file.txt";
220+
221+
// spell-checker:disable
222+
let input = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ".repeat(50);
223+
// spell-checker:enable
224+
at.write(filename, &input);
225+
226+
let result = ucmd.arg("--base58").arg(filename).succeeds();
227+
let encoded = result.stdout_str();
228+
229+
// Verify the output ends with the expected suffix (matches GNU basenc output)
230+
// spell-checker:disable
231+
assert!(
232+
encoded
233+
.trim_end()
234+
.ends_with("ZNRRacEnhrY83ZEYkpwWVZNFK5DFRasr\nw693NsNGtiQ9fYAj")
235+
);
236+
// spell-checker:enable
237+
}
238+
214239
#[test]
215240
fn test_choose_last_encoding_base64() {
216241
new_ucmd!()

0 commit comments

Comments
 (0)