pw_varint: MaxValueInBytes function

This adds a function to the varint API that returns the maximum value
that can be encoded into a given number of bytes.

Change-Id: I6be36117623e3cb9ec90fc4c5eb88b42196385aa
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/39581
Commit-Queue: Alexei Frolov <frolv@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_varint/docs.rst b/pw_varint/docs.rst
index 3327ccd..58c90f9 100644
--- a/pw_varint/docs.rst
+++ b/pw_varint/docs.rst
@@ -28,6 +28,11 @@
 
 Returns the size of a signed integer when ZigZag encoded as a varint.
 
+.. cpp:function:: uint64_t MaxValueInBytes(size_t bytes)
+
+Returns the maximum integer value that can be encoded as a varint into the
+specified number of bytes.
+
 Dependencies
 ============
 * ``pw_span``
diff --git a/pw_varint/public/pw_varint/varint.h b/pw_varint/public/pw_varint/varint.h
index 4432e19..9d0e0ce 100644
--- a/pw_varint/public/pw_varint/varint.h
+++ b/pw_varint/public/pw_varint/varint.h
@@ -198,6 +198,29 @@
   return EncodedSize(ZigZagEncode(integer));
 }
 
+// Returns the maximum integer value that can be encoded in a varint of the
+// specified number of bytes.
+//
+// These values are also listed in the table below. Zigzag encoding cuts these
+// in half, as positive and negative integers are alternated.
+//
+//   Bytes          Max value
+//     1                          127
+//     2                       16,383
+//     3                    2,097,151
+//     4                  268,435,455
+//     5               34,359,738,367 -- needed for max uint32 value
+//     6            4,398,046,511,103
+//     7          562,949,953,421,311
+//     8       72,057,594,037,927,935
+//     9    9,223,372,036,854,775,807
+//     10            uint64 max value
+//
+constexpr uint64_t MaxValueInBytes(size_t bytes) {
+  return bytes >= kMaxVarint64SizeBytes ? std::numeric_limits<uint64_t>::max()
+                                        : (uint64_t(1) << (7 * bytes)) - 1;
+}
+
 }  // namespace varint
 }  // namespace pw
 
diff --git a/pw_varint/varint_test.cc b/pw_varint/varint_test.cc
index 2d373d2..27282c4 100644
--- a/pw_varint/varint_test.cc
+++ b/pw_varint/varint_test.cc
@@ -1054,5 +1054,29 @@
   EXPECT_EQ(ZigZagEncodedSize(std::numeric_limits<int64_t>::max()), 10u);
 }
 
+constexpr uint64_t CalculateMaxValueInBytes(size_t bytes) {
+  uint64_t value = 0;
+  for (size_t i = 0; i < bytes; ++i) {
+    value |= uint64_t(0x7f) << (7 * i);
+  }
+  return value;
+}
+
+TEST(Varint, MaxValueInBytes) {
+  static_assert(MaxValueInBytes(0) == 0);
+  static_assert(MaxValueInBytes(1) == 0x7f);
+  static_assert(MaxValueInBytes(2) == 0x3fff);
+  static_assert(MaxValueInBytes(3) == 0x1fffff);
+  static_assert(MaxValueInBytes(4) == 0x0fffffff);
+  static_assert(MaxValueInBytes(5) == CalculateMaxValueInBytes(5));
+  static_assert(MaxValueInBytes(6) == CalculateMaxValueInBytes(6));
+  static_assert(MaxValueInBytes(7) == CalculateMaxValueInBytes(7));
+  static_assert(MaxValueInBytes(8) == CalculateMaxValueInBytes(8));
+  static_assert(MaxValueInBytes(9) == CalculateMaxValueInBytes(9));
+  static_assert(MaxValueInBytes(10) == std::numeric_limits<uint64_t>::max());
+  static_assert(MaxValueInBytes(11) == std::numeric_limits<uint64_t>::max());
+  static_assert(MaxValueInBytes(100) == std::numeric_limits<uint64_t>::max());
+}
+
 }  // namespace
 }  // namespace pw::varint