[git commit] libbb/yescrypt: explain and shrink decode64_uint32()

Denys Vlasenko vda.linux at googlemail.com
Tue Jul 8 01:47:48 UTC 2025


commit: https://git.busybox.net/busybox/commit/?id=78bd8a44b75cf39ea17c8a586ba35776d835551f
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
decode64_uint32                                      177     141     -36

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 libbb/yescrypt/alg-yescrypt-common.c | 87 +++++++++++++++++++++++++++++++-----
 libbb/yescrypt/alg-yescrypt.h        |  2 +
 2 files changed, 79 insertions(+), 10 deletions(-)

diff --git a/libbb/yescrypt/alg-yescrypt-common.c b/libbb/yescrypt/alg-yescrypt-common.c
index e5d045058..c85b2a0b9 100644
--- a/libbb/yescrypt/alg-yescrypt-common.c
+++ b/libbb/yescrypt/alg-yescrypt-common.c
@@ -27,41 +27,106 @@ static NOINLINE const uint8_t *decode64_uint32(
 		uint32_t *dst,
 		const uint8_t *src, uint32_t val)
 {
-	uint32_t start = 0, end = 47, chars = 1, bits = 0;
+	uint32_t start = 0, end = 47, bits = 0;
 	uint32_t c;
 
-	if (!src) /* prevous decode failed already? */
+	if (!src) /* previous decode failed already? */
 		goto fail;
 
 	c = a2i64(*src++);
 	if (c > 63)
 		goto fail;
 
+// The encoding of number N:
+// start = 0 end = 47
+// If N < 48, it is encoded verbatim, else
+// N -= 48
+// start = end+1 = 48
+// end += (64-end)/2 = 55
+// If N < (end+1-start)<<6 = 8<<6, it is encoded as 48+(N>>6)|low6bits (that is, 48...55|<6bit>), else
+// N -= 8<<6
+// start = end+1 = 56
+// end += (64-end)/2 = 59
+// If N < (end+1-start)<<2*6 = 4<<12, it is encoded as 56+(N>>2*6)|low12bits (that is, 56...59|<6bit>|<6bit>), else
+// ...same for 60..61|<6bit>|<6bit>|<6bit>
+// .......same for 62|<6bit>|<6bit>|<6bit>|<6bit>
+// .......same for 63|<6bit>|<6bit>|<6bit>|<6bit>|<6bit>
+	dbg_dec64("c:%d val:0x%08x", (int)c, (unsigned)val);
 	while (c > end) {
+		dbg_dec64("c:%d > end:%d", (int)c, (int)end);
 		val += (end + 1 - start) << bits;
+		dbg_dec64("val+=0x%08x", (int)((end + 1 - start) << bits));
+		dbg_dec64(" val:0x%08x", (unsigned)val);
 		start = end + 1;
-		end = start + (62 - end) / 2;
-		chars++;
+		end += (64 - end) / 2;
 		bits += 6;
+		dbg_dec64("start=%d", (int)start);
+		dbg_dec64("end=%d", (int)end);
+		dbg_dec64("bits=%d", (int)bits);
 	}
 
 	val += (c - start) << bits;
+	dbg_dec64("final val+=0x%08x", (int)((c - start) << bits));
+	dbg_dec64("       val:0x%08x", (unsigned)val);
 
-	while (--chars) {
+	while (bits != 0) {
 		c = a2i64(*src++);
 		if (c > 63)
 			goto fail;
 		bits -= 6;
 		val += c << bits;
+		dbg_dec64("low bits val+=0x%08x", (int)(c << bits));
+		dbg_dec64("          val:0x%08x", (unsigned)val);
 	}
+ ret:
 	*dst = val;
-
 	return src;
+ fail:
+	val = 0;
+	src = NULL;
+	goto ret;
+}
 
-fail:
-	*dst = 0;
-	return NULL;
+#if TEST_DECODE64
+static void test_decode64_uint32(void)
+{
+	const uint8_t *src, *end;
+	uint32_t u32;
+	int a = 48;
+	int b = 8<<6;  // 0x0200
+	int c = 4<<12; // 0x04000
+	int d = 2<<18; // 0x080000
+	int e = 1<<24; // 0x1000000
+
+	src = (void*)"wzzz";
+	end = decode64_uint32(&u32, src, 0);
+	if (u32 != 0x0003ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
+	if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
+	src = (void*)"xzzz";
+	end = decode64_uint32(&u32, src, 0);
+	if (u32 != 0x0007ffff+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
+	if (end != src + 4) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
+	// Note how the last representable "x---" encoding, 0x7ffff, is exactly d-1!
+	// And if we now increment it, we get:
+	src = (void*)"y....";
+	end = decode64_uint32(&u32, src, 0);
+	if (u32 != 0x00000000+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
+	if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
+	src = (void*)"yzzzz";
+	end = decode64_uint32(&u32, src, 0);
+	if (u32 != 0x00ffffff+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
+	if (end != src + 5) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
+
+	src = (void*)"zzzzzz";
+	end = decode64_uint32(&u32, src, 0);
+	if (u32 != 0x3fffffff+e+d+c+b+a) bb_error_msg_and_die("Incorrect decode '%s':0x%08x", src, (unsigned)u32);
+	if (end != src + 6) bb_error_msg_and_die("Incorrect decode '%s': %p end:%p", src, src, end);
+
+	bb_error_msg("test_decode64_uint32() OK");
 }
+#else
+# define test_decode64_uint32() ((void)0)
+#endif
 
 #if 1
 static const uint8_t *decode64(
@@ -70,7 +135,7 @@ static const uint8_t *decode64(
 {
 	size_t dstpos = 0;
 
-	dbg_dec64("src:'%s' len:%d", src, (int)srclen);
+	dbg_dec64("src:'%s'", src);
 	for (;;) {
 		uint32_t c, value = 0;
 		int bits = 0;
@@ -215,6 +280,8 @@ char *yescrypt_r(
 	size_t need, prefixlen;
 	uint32_t u32;
 
+	test_decode64_uint32();
+
 	memset(yctx, 0, sizeof(yctx));
 	yctx->param.p = 1;
 
diff --git a/libbb/yescrypt/alg-yescrypt.h b/libbb/yescrypt/alg-yescrypt.h
index 4554e3de3..e558cfdc5 100644
--- a/libbb/yescrypt/alg-yescrypt.h
+++ b/libbb/yescrypt/alg-yescrypt.h
@@ -28,6 +28,7 @@
  * online backup system.
  */
 #ifdef YESCRYPT_INTERNAL
+
 # if 1
 #  define dbg(...) ((void)0)
 # else
@@ -39,6 +40,7 @@
 #  define dbg_dec64(...) bb_error_msg(__VA_ARGS__)
 # endif
 #endif
+#define TEST_DECODE64 0
 
 /**
  * Type and possible values for the flags argument of yescrypt_kdf(),


More information about the busybox-cvs mailing list