Branch-free varint size calculation. On android art, compiles to:

```
int com.google.android.test.Outer.computeUInt32SizeNoTag(int) [24 bytes]
    0x000024d0    mov w0, #0x160
    0x000024d4    clz w1, w1
    0x000024d8    add w1, w1, w1, lsl #3
    0x000024dc    sub w0, w0, w1
    0x000024e0    lsr w0, w0, #6
    0x000024e4    ret
```
versus existing:
```
int com.google.android.test.Outer.computeUInt32SizeNoTag(int) [72 bytes]
    0x000022a0    and w0, w1, #0xffffff80
    0x000022a4    cbnz w0, #+0xc (addr 0x22b0)
    0x000022a8    mov w0, #0x1
    0x000022ac    b #+0x38 (addr 0x22e4)
    0x000022b0    and w0, w1, #0xffffc000
    0x000022b4    cbnz w0, #+0xc (addr 0x22c0)
    0x000022b8    mov w0, #0x2
    0x000022bc    b #+0x28 (addr 0x22e4)
    0x000022c0    and w0, w1, #0xffe00000
    0x000022c4    cbnz w0, #+0xc (addr 0x22d0)
    0x000022c8    mov w0, #0x3
    0x000022cc    b #+0x18 (addr 0x22e4)
    0x000022d0    mov w2, #0x5
    0x000022d4    mov w0, #0x4
    0x000022d8    and w1, w1, #0xf0000000
    0x000022dc    cmp w1, #0x0 (0)
    0x000022e0    csel w0, w2, w0, ne
    0x000022e4    ret
```

PiperOrigin-RevId: 591113652
diff --git a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
index 37bb44a..cd8127b 100644
--- a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -40,7 +40,9 @@
   /** Used to adapt to the experimental {@link Writer} interface. */
   CodedOutputStreamWriter wrapper;
 
-  /** @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead. */
+  /**
+   * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead.
+   */
   @Deprecated public static final int LITTLE_ENDIAN_32_SIZE = FIXED32_SIZE;
 
   /** The buffer size used in {@link #newInstance(OutputStream)}. */
@@ -669,9 +671,8 @@
   }
 
   /**
-   * Compute the number of bytes that would be needed to encode a lazily parsed MessageSet
-   * extension field to the stream. For historical reasons, the wire format differs from normal
-   * fields.
+   * Compute the number of bytes that would be needed to encode a lazily parsed MessageSet extension
+   * field to the stream. For historical reasons, the wire format differs from normal fields.
    */
   public static int computeLazyFieldMessageSetExtensionSize(
       final int fieldNumber, final LazyFieldLite value) {
@@ -692,29 +693,52 @@
    * tag.
    */
   public static int computeInt32SizeNoTag(final int value) {
-    if (value >= 0) {
-      return computeUInt32SizeNoTag(value);
-    } else {
-      // Must sign-extend.
-      return MAX_VARINT_SIZE;
-    }
+    return computeUInt64SizeNoTag((long) value);
   }
 
   /** Compute the number of bytes that would be needed to encode a {@code uint32} field. */
   public static int computeUInt32SizeNoTag(final int value) {
-    if ((value & (~0 << 7)) == 0) {
-      return 1;
-    }
-    if ((value & (~0 << 14)) == 0) {
-      return 2;
-    }
-    if ((value & (~0 << 21)) == 0) {
-      return 3;
-    }
-    if ((value & (~0 << 28)) == 0) {
-      return 4;
-    }
-    return 5;
+    /*
+    This code is ported from the C++ varint implementation.
+    Implementation notes:
+
+    To calcuate varint size, we want to count the number of 7 bit chunks required. Rather than using
+    division by 7 to accomplish this, we use multiplication by 9/64. This has a number of important
+    properties:
+     * It's roughly 1/7.111111. This makes the 0 bits set case have the same value as the 7 bits set
+       case, so offsetting by 1 gives us the correct value we want for integers up to 448 bits.
+     * Multiplying by 9 is special. x * 9 = x << 3 + x, and so this multiplication can be done by a
+       single shifted add on arm (add w0, w0, w0, lsl #3), or a single lea instruction
+       (leal (%rax,%rax,8), %eax)) on x86.
+     * Dividing by 64 is a 6 bit right shift.
+
+    An explicit non-sign-extended right shift is used instead of the more obvious '/ 64' because
+    that actually produces worse code on android arm64 at time of authoring because of sign
+    extension. Rather than
+        lsr w0, w0, #6
+    It would emit:
+        add w16, w0, #0x3f (63)
+        cmp w0, #0x0 (0)
+        csel w0, w16, w0, lt
+        asr w0, w0, #6
+
+    Summarized:
+    floor(((Integer.SIZE - clz) / 7.1111) + 1
+    ((Integer.SIZE - clz) * 9) / 64 + 1
+    (((Integer.SIZE - clz) * 9) >>> 6) + 1
+    ((Integer.SIZE - clz) * 9 + (1 << 6)) >>> 6
+    (Integer.SIZE * 9 + (1 << 6) - clz * 9) >>> 6
+    (352 - clz * 9) >>> 6
+    on arm:
+    (352 - clz - (clz << 3)) >>> 6
+    on x86:
+    (352 - lea(clz, clz, 8)) >>> 6
+
+    If you make changes here, please validate their compiled output on different architectures and
+    runtimes.
+    */
+    int clz = Integer.numberOfLeadingZeros(value);
+    return ((Integer.SIZE * 9 + (1 << 6)) - (clz * 9)) >>> 6;
   }
 
   /** Compute the number of bytes that would be needed to encode an {@code sint32} field. */
@@ -745,27 +769,9 @@
    * tag.
    */
   public static int computeUInt64SizeNoTag(long value) {
-    // handle two popular special cases up front ...
-    if ((value & (~0L << 7)) == 0L) {
-      return 1;
-    }
-    if (value < 0L) {
-      return 10;
-    }
-    // ... leaving us with 8 remaining, which we can divide and conquer
-    int n = 2;
-    if ((value & (~0L << 35)) != 0L) {
-      n += 4;
-      value >>>= 28;
-    }
-    if ((value & (~0L << 21)) != 0L) {
-      n += 2;
-      value >>>= 14;
-    }
-    if ((value & (~0L << 14)) != 0L) {
-      n += 1;
-    }
-    return n;
+    int clz = Long.numberOfLeadingZeros(value);
+    // See computeUInt32SizeNoTag for explanation
+    return ((Long.SIZE * 9 + (1 << 6)) - (clz * 9)) >>> 6;
   }
 
   /** Compute the number of bytes that would be needed to encode an {@code sint64} field. */
diff --git a/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
index 3007c83..51e66b9 100644
--- a/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
+++ b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
@@ -327,6 +327,81 @@
         .isEqualTo(-75123905439571256L);
   }
 
+  @Test
+  public void computeIntSize() {
+    assertThat(CodedOutputStream.computeUInt32SizeNoTag(0)).isEqualTo(1);
+    assertThat(CodedOutputStream.computeUInt64SizeNoTag(0)).isEqualTo(1);
+    int i;
+    for (i = 0; i < 7; i++) {
+      assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(1);
+      assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(1);
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(1);
+    }
+    for (; i < 14; i++) {
+      assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(2);
+      assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(2);
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(2);
+    }
+    for (; i < 21; i++) {
+      assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(3);
+      assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(3);
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(3);
+    }
+    for (; i < 28; i++) {
+      assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(4);
+      assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(4);
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(4);
+    }
+    for (; i < 31; i++) {
+      assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(5);
+      assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(5);
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(5);
+    }
+    for (; i < 32; i++) {
+      assertThat(CodedOutputStream.computeInt32SizeNoTag(1 << i)).isEqualTo(10);
+      assertThat(CodedOutputStream.computeUInt32SizeNoTag(1 << i)).isEqualTo(5);
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(5);
+    }
+    for (; i < 35; i++) {
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(5);
+    }
+    for (; i < 42; i++) {
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(6);
+    }
+    for (; i < 49; i++) {
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(7);
+    }
+    for (; i < 56; i++) {
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(8);
+    }
+    for (; i < 63; i++) {
+      assertThat(CodedOutputStream.computeUInt64SizeNoTag(1L << i)).isEqualTo(9);
+    }
+  }
+
+  @Test
+  public void computeTagSize() {
+    assertThat(CodedOutputStream.computeTagSize(0)).isEqualTo(1);
+    int i;
+    for (i = 0; i < 4; i++) {
+      assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(1);
+    }
+    for (; i < 11; i++) {
+      assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(2);
+    }
+    for (; i < 18; i++) {
+      assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(3);
+    }
+    for (; i < 25; i++) {
+      assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(4);
+    }
+    for (; i < 29; i++) {
+      assertThat(CodedOutputStream.computeTagSize(1 << i)).isEqualTo(5);
+    }
+    // Invalid tags
+    assertThat(CodedOutputStream.computeTagSize((1 << 30) + 1)).isEqualTo(1);
+  }
+
   /** Tests writing a whole message with every field type. */
   @Test
   public void testWriteWholeMessage() throws Exception {