Commit graph

1518 commits

Author SHA1 Message Date
Protobuf Team Bot
1ea0ff394b Remove redundant memoizedSize member on DynamicMessage
The base class already has this int, this one just confusingly shadows the base class variable and makes it so one can be memoized and the other one not (they can never be a different value and both set).

This makes DynamicMessage behavior match gencode, where the gencode uses the superclass's memoizedSize variable.

PiperOrigin-RevId: 886813561
2026-03-20 08:48:51 -07:00
Mark Hansen
6ccda5eb20 Extract never-happens exception throwing in MessageSchema.
To make the method smaller and faster. The hot-path assembly code is substantially smaller if it doesn't have to allocate (and handle allocation failure) and throw the exception directly.

Take this example: https://godbolt.org/z/EGYefWMEG

```java
class Square {
    public int throwDirectly(int a) {
        switch (a) {
            case 1: return 1;
        }
            throw new IllegalArgumentException();
    }

    public int throwViaMethod(int a) {
        switch (a) {
            case 1: return 1;
        }
            return throwIllegalArgumentException();
    }

    public static int throwIllegalArgumentException() {
      throw new IllegalArgumentException();
    }
}
```

Outputs dex:

```
# virtual methods
.method public throwDirectly(I)I
    .registers 2

    #@0
    .line 3
    packed-switch p1, :pswitch_data_c

    #@3
    .line 6
    new-instance p1, Ljava/lang/IllegalArgumentException;

    #@5
    invoke-direct {p1}, Ljava/lang/IllegalArgumentException;-><init>()V

    #@8
    throw p1

    #@9
    .line 4
    :pswitch_9
    const/4 p1, 0x1

    #@a
    return p1

    #@b
    nop

    #@c
    :pswitch_data_c
    .packed-switch 0x1
        :pswitch_9
    .end packed-switch
.end method

.method public throwViaMethod(I)I
    .registers 2

    #@0
    .line 10
    packed-switch p1, :pswitch_data_a

    #@3
    .line 13
    invoke-static {}, LSquare;->throwIllegalArgumentException()I

    #@6
    move-result p1

    #@7
    return p1

    #@8
    .line 11
    :pswitch_8
    const/4 p1, 0x1

    #@9
    return p1

    #@a
    :pswitch_data_a
    .packed-switch 0x1
        :pswitch_8
    .end packed-switch
.end method
```

Which compiles to substantially smaller oat code (100 bytes before, 52 bytes after):

```
int Square.throwDirectly(int) [100 bytes]
    0x00004070    sub x16, sp, #0x2000 (8192)
    0x00004074    ldr wzr, [x16]
     StackMap[0]   native_pc=0x4078, dex_pc=0x0, register_mask=0x0, stack_mask=0b
    0x00004078    str x0, [sp, #-32]!
    0x0000407c    stp x22, lr, [sp, #16]
    0x00004080    ldr x21, [x21]
     StackMap[1]   native_pc=0x4084, dex_pc=0x0, register_mask=0x2, stack_mask=0b
    0x00004084    cmp w2, #0x1 (1)
    0x00004088    b.ne #+0x14 (addr 0x0000409c)
    0x0000408c    mov w0, #0x1
    0x00004090    ldp x22, lr, [sp, #16]
    0x00004094    add sp, sp, #0x20 (32)
    0x00004098    ret
    0x0000409c    adrp x0, #+0x4000 (addr 0x00008000)
    0x000040a0    ldr w0, [x0]
    0x000040a4    ldr lr, [tr, #464] ; pAllocObjectInitialized
    0x000040a8    blr lr
     StackMap[2]   native_pc=0x40ac, dex_pc=0x4, register_mask=0x0, stack_mask=0b
    0x000040ac    dmb ishst
    0x000040b0    mov x1, x0
    0x000040b4    mov x22, x1
    0x000040b8    adrp x0, #+0x4000 (addr 0x00008000)
    0x000040bc    ldr w0, [x0, #4]
    0x000040c0    ldr lr, [x0, #24]
    0x000040c4    blr lr
     StackMap[3]   native_pc=0x40c8, dex_pc=0x6, register_mask=0x400000, stack_mask=0b
    0x000040c8    mov x0, x22
    0x000040cc    ldr lr, [tr, #1264] ; pDeliverException
    0x000040d0    blr lr
     StackMap[4]   native_pc=0x40d4, dex_pc=0x9, register_mask=0x400000, stack_mask=0b

int Square.throwViaMethod(int) [52 bytes]
    0x000040e0    sub x16, sp, #0x2000 (8192)
    0x000040e4    ldr wzr, [x16]
     StackMap[0]   native_pc=0x40e8, dex_pc=0x0, register_mask=0x0, stack_mask=0b
    0x000040e8    stp x0, lr, [sp, #-16]!
    0x000040ec    ldr x21, [x21]
     StackMap[1]   native_pc=0x40f0, dex_pc=0x0, register_mask=0x2, stack_mask=0b
    0x000040f0    cmp w2, #0x1 (1)
    0x000040f4    b.eq #+0x14 (addr 0x00004108)
    0x000040f8    adrp x0, #+0x8000 (addr 0x0000c000)
    0x000040fc    ldr x0, [x0]
    0x00004100    ldr lr, [x0, #24]
    0x00004104    blr lr
     StackMap[2]   native_pc=0x4108, dex_pc=0x3, register_mask=0x0, stack_mask=0b
    0x00004108    mov w0, #0x1
    0x0000410c    ldp xzr, lr, [sp], #16
    0x00004110    ret
```

PiperOrigin-RevId: 884663197
2026-03-16 15:28:56 -07:00
Protobuf Team Bot
7f31895c84 Delete the experimental BinaryWriter class.
This was for the experimental runtime, which is now defunct and we don't have any immediate plan to resurrect.

Removing this will unlock the ability to remove FieldOrder.DESCENDING in a followup change, which will result in benefits to the in-real-use paths for Android which currently have paths for both ASCENDING and DESCENDING.

PiperOrigin-RevId: 884389156
2026-03-16 05:54:01 -07:00
Protobuf Team Bot
4efe3af06b Automated rollback of commit ab531655c3.
PiperOrigin-RevId: 881454470
2026-03-10 08:49:10 -07:00
Liam Miller-Cushon
ab531655c3 Use String#encodedLength on JDK versions that support it
See https://bugs.openjdk.org/browse/JDK-8372353

PiperOrigin-RevId: 881440323
2026-03-10 08:18:57 -07:00
Protobuf Team Bot
cb3b3c9614 Small optimizations in MessageSchema.java.
- Reuse existing local variables for field offsets in isFieldPresent.
- Avoid redundant bitwise operations in isOneofCaseEqual and setOneofPresent.
- Move numberAt(pos) inside the switch cases where it is actually used in isInitialized.

PiperOrigin-RevId: 878665459
2026-03-04 14:23:08 -08:00
Protobuf Team Bot
de8f3be728 Optimize hashCode similar to how we optimized equals.
PiperOrigin-RevId: 877562727
2026-03-02 13:57:46 -08:00
Protobuf Team Bot
5bfc1a239d Make Java lite equals much more efficient for oneofs.
This fixes https://github.com/protocolbuffers/protobuf/issues/26058

Mechanically, this adds tracking to MessageSchema in the intArray to include bufferIndex offsets of one member of each oneof in a message. Then in the equals function, when iterating all fields, we skip the oneof fields and do a supplemental pass over the intArray to cover the representative members of the oneofs.

In the course of adding this, I noticed we no longer needed the intArray to track repeated fields so I removed that. This means that this change is likely a net improvement in memory usage for MessageSchemas too as the number of oneofs in the universe of proto is likely much smaller than the number of repeated fields.

PiperOrigin-RevId: 877446720
2026-03-02 09:55:44 -08:00
Mark Hansen
3d9471b823 Make GeneratedMessageLite.defaultInstanceMap final
This might help optimisers to inline it.

PiperOrigin-RevId: 877153922
2026-03-01 19:27:15 -08:00
Mark Hansen
8f145a01e9 Make GeneratedMessageLite.buildMessageInfo final
This helps with devirtualizing calls and inlining

PiperOrigin-RevId: 877148384
2026-03-01 19:05:04 -08:00
Mark Hansen
9ddc4da98e Make GeneratedMessageLite.getMemoizedSerializedSize final
This improves opportunities for devirtualization and inlining.

PiperOrigin-RevId: 877143673
2026-03-01 18:43:54 -08:00
Mark Hansen
d1183f9ef2 Make GeneratedMessageLite.clearMemoizedSerializedSize final
This improves opportunities for devirtualization and inlining.

PiperOrigin-RevId: 877138401
2026-03-01 18:24:24 -08:00
Mark Hansen
a5e658d787 Make GeneratedMessageLite.makeImmutable final
This allows more devirtualization and inlining of calls.

PiperOrigin-RevId: 877133161
2026-03-01 18:03:14 -08:00
Mark Hansen
71bcf67aa6 Make GeneratedMessageLite.mergeLengthDelimitedField final
This improves opportunities for devirtualizing and inlining calls.

PiperOrigin-RevId: 877129249
2026-03-01 17:40:50 -08:00
Mark Hansen
e4655ff58c Make GeneratedMessageLite.mergeVarintField final
This improves opportunities for devirtualization and inlining.

PiperOrigin-RevId: 877124484
2026-03-01 17:17:02 -08:00
Mark Hansen
d77766667a Make GeneratedMessageLite.parseUnknownField final
This allows more devirtualization and inlining of calls.

PiperOrigin-RevId: 877119954
2026-03-01 16:54:33 -08:00
Mark Hansen
fa128ee88b Make GeneratedMessageLite.computeHashCode final
This allows optimizers to more-easily devirtualize calls or inline them.

PiperOrigin-RevId: 877115501
2026-03-01 16:32:49 -08:00
Mark Hansen
ee55b8834b Make GeneratedMessageLite.newMutableInstance final
This allows optimisers to devirtualize or inline this.

PiperOrigin-RevId: 877110462
2026-03-01 16:09:21 -08:00
Mark Hansen
9cb483dd8a Tag GeneratedMessageLite.hashCodeIsNotMemoized as final
It's effectively final. Tagging it as such helps human readers and
optimizers decide to devirtualize calls into here.

PiperOrigin-RevId: 877106322
2026-03-01 15:47:41 -08:00
Mark Hansen
e264e7965b Tag GeneratedMessageLite.clearMemorizedHashCode as final
This will help optimisers devirtualize calls to this method.

PiperOrigin-RevId: 877102391
2026-03-01 15:24:25 -08:00
Mark Hansen
3bded241e2 Tag GeneratedMessageLite.setMemoizedHashCode as final
It's effectively final. Making it actually final helps optimizers to
make devirtualization decisions.

PiperOrigin-RevId: 877101278
2026-03-01 15:19:28 -08:00
Mark Hansen
9e6c5775b9 Tag GeneratedMessageLite.getMemoizedHashCode as final
This is effectively-final. Tagging it as final will help optimizers make
devirtualization decisions.

PiperOrigin-RevId: 877095927
2026-03-01 14:55:24 -08:00
Protobuf Team Bot
3c6858ca1d Automated Code Change
PiperOrigin-RevId: 877071711
2026-03-01 12:58:33 -08:00
Protobuf Team Bot
94c68efaaa This change does not affect OSS
PiperOrigin-RevId: 876331033
2026-02-27 10:58:09 -08:00
Protobuf Team Bot
365bde3e5f Introduce the ability to print extensions within Any proto by passing an ExtensionRegistry to the Printer.
This method is held back from the public API for now.

PiperOrigin-RevId: 875795864
2026-02-26 10:59:01 -08:00
Protobuf Team Bot
09adc5e52d Internal
PiperOrigin-RevId: 875718606
2026-02-26 07:45:36 -08:00
Mark Hansen
079bddd6e6 Tag GeneratedMessageLite.markImmutable as final
This method is effectively-final. Tagging it as actually final will help
optimisers to devirtualize the call.

PiperOrigin-RevId: 875406002
2026-02-25 17:16:24 -08:00
Mark Hansen
9ac1f075cf Make GeneratedMessageLite.isMutable final
This is a hot method, and tagging it as final helps the compiler to
realise it may be able to inline it.

PiperOrigin-RevId: 875401389
2026-02-25 17:03:14 -08:00
Mark Hansen
6568c00fea Replace an expensive checkNotNull with a cheap one.
PiperOrigin-RevId: 875346290
2026-02-25 14:49:48 -08:00
Mark Hansen
ea0586c858 Replace an expensive null throw with a cheap one in MessageSchema.mergeFrom
mergeFrom is a very hot method.

R8 can turn this into getClass() call.

PiperOrigin-RevId: 875335888
2026-02-25 14:27:04 -08:00
Protobuf Team Bot
b843769d72 Introduce the ability to print fully qualified extension names and allow passing an ExtensionRegistry to the Parser.
This fixes as issue where extension names were incorrectly printed using their short name which prevents parsing and can lead to ambiguous or duplicate json keys.

This feature is gated by a boolean options printingFullyQualifiedExtensionNames and printingShortExtensionNames for testing. These methods are held back from the public API for now.

PiperOrigin-RevId: 875322649
2026-02-25 13:56:32 -08:00
Mark Hansen
841381331c Specialise ProtobufArrayList.equals,hashCode to avoid .get() stack frame
When the superclass AbstractProtobufList implemented equals, it has to
call virtual .get(i), which can't be inlined because it's not sure which
subclass to inline.

If we put equals in the subclass ProtobufArrayList, then we can use
array index access rather than .get(i), which should be faster.

I think it's possible that rearranging the order of the if-statements in the equals might yield faster results (e.g. maybe we should check if the `other` is `ProtobufArrayList` up front rather than first checking instanceof List and instanceof RandomAccess, which has to loop walking the array of implemented interfaces), but this is a reasonable start; at least as fast as the existing implementation.

PiperOrigin-RevId: 874813296
2026-02-24 15:20:18 -08:00
Mark Hansen
2c2f379887 Skip checkNotNull inside Protobuf.schemaFor
The next line: ConcurrentHashMap.get, already checks if the argument is
null.

This should save us one instruction on the hot path of this hot method.

Before:

```
    CODE: (code_offset=0x00368e80 size=192)...
      0x00368e80: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00368e84: b940021f  ldr wzr, [x16]
        StackMap[0] (native_pc=0x368e88, dex_pc=0x0, register_mask=0x0, stack_mask=0b)
      0x00368e88: f81d0fe0  str x0, [sp, #-48]!
      0x00368e8c: a9015ff6  stp x22, x23, [sp, #16]
      0x00368e90: a9027bf8  stp x24, lr, [sp, #32]
      0x00368e94: f94002b5  ldr x21, [x21]
        StackMap[1] (native_pc=0x368e98, dex_pc=0x0, register_mask=0x6, stack_mask=0b)
      0x00368e98: b940005f  ldr wzr, [x2]
```

The last line here will disappear. After:

```
    CODE: (code_offset=0x00368d40 size=188)...
      0x00368d40: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00368d44: b940021f  ldr wzr, [x16]
        StackMap[0] (native_pc=0x368d48, dex_pc=0x0, register_mask=0x0, stack_mask=0b)
      0x00368d48: f81d0fe0  str x0, [sp, #-48]!
      0x00368d4c: a9015ff6  stp x22, x23, [sp, #16]
      0x00368d50: a9027bf8  stp x24, lr, [sp, #32]
      0x00368d54: f94002b5  ldr x21, [x21]
```

PiperOrigin-RevId: 874804725
2026-02-24 14:58:49 -08:00
Mark Hansen
a26f258a66 Move cast to end of function
Avoids us checking the cast if we're just going to go down the null branch.

Before: bigger code, 200 bytes:

```
CODE: (code_offset=0x00368fe0 size=200)...
    DEX CODE:
      0x0000: 6e10 9c16 0200            | invoke-virtual {v2}, java.lang.Class java.lang.Object.getClass() // method@5788
      0x0003: 5410 590f                 | iget-object v0, v1, j$.util.concurrent.ConcurrentHashMap pd.c // field@3929
      0x0005: 6e20 6515 2000            | invoke-virtual {v0, v2}, java.lang.Object j$.util.concurrent.ConcurrentHashMap.get(java.lang.Object) // method@5477
      0x0008: 0c00                      | move-result-object v0
      0x0009: 1f00 dc07                 | check-cast v0, pg // type@TypeIndex[2012]
      0x000b: 3900 0700                 | if-nez v0, +7
      0x000d: 7020 6024 2100            | invoke-direct {v1, v2}, pg pd.c(java.lang.Class) // method@9312
      0x0010: 0c01                      | move-result-object v1
      0x0011: 1101                      | return-object v1
      0x0012: 1100                      | return-object v0
```

After, the code is smaller (192 bytes):

```
CODE: (code_offset=0x00368e80 size=192)...
    DEX CODE:                                                                                                                                                        0x0000: 6e10 9c16 0200            | invoke-virtual {v2}, java.lang.Class java.lang.Object.getClass() // method@5788                                            0x0003: 5410 590f                 | iget-object v0, v1, j$.util.concurrent.ConcurrentHashMap pd.c // field@3929                                                0x0005: 6e20 6515 2000            | invoke-virtual {v0, v2}, java.lang.Object j$.util.concurrent.ConcurrentHashMap.get(java.lang.Object) // method@5477        0x0008: 0c00                      | move-result-object v0                                                                                                      0x0009: 3900 0700                 | if-nez v0, +7
      0x000b: 7020 6024 2100            | invoke-direct {v1, v2}, pg pd.c(java.lang.Class) // method@9312
      0x000e: 0c01                      | move-result-object v1
      0x000f: 1101                      | return-object v1
      0x0010: 1f00 dc07                 | check-cast v0, pg // type@TypeIndex[2012]
      0x0012: 1100                      | return-object v0
```

This is probably a minor optimisation: the null branch is slow anyway.

PiperOrigin-RevId: 874803534
2026-02-24 14:54:33 -08:00
Protobuf Team Bot
57093a8bd5 Avoid toBigIntegerExact in JsonFormat to avoid degenerate parse behavior in the face of large exponents.
https://github.com/protocolbuffers/protobuf/issues/26032

PiperOrigin-RevId: 874768607
2026-02-24 13:41:47 -08:00
Mark Hansen
6f67f26c40 Extract slow path function for schemaFor
schemaFor is a very hot method, called for every message we process. We only fall through to creating the schema the first time for each class, thereafter, the hot path is just getting from the ConcurrentHashMap.

Avoid R8 inlining the slow (less common) path of schemaFor. The inlining blows up the stack frame size and slows down the hot path.

PiperOrigin-RevId: 874384247
2026-02-23 22:37:59 -08:00
Mark Hansen
e8f93acee8 Devirtualize calls to schemaFactory
By specifying the field as its final type

This might speed up schema construction. Schema constructions is the cold path, but it's important at app startup. This is probably not going to make a big difference to benchmarks, just an opportunistic improvement.

PiperOrigin-RevId: 873770482
2026-02-22 15:38:52 -08:00
Mark Hansen
043a2e1b35 Use ConcurrentHashMap not ConcurrentMap
To avoid interface indirection in hot function schemaFor.

PiperOrigin-RevId: 873765872
2026-02-22 15:16:04 -08:00
Protobuf Team Bot
bbcc021add Add tests for JsonFormat extension printing.
This change adds base tests case for printing proto2 extensions. It highlights a known issue with parsing extensions as short names and duplicated field names.

PiperOrigin-RevId: 873022900
2026-02-20 12:23:08 -08:00
Protobuf Team Bot
f5626fa680 Add streaming bytes read support to CodedInputStream
PiperOrigin-RevId: 872590864
2026-02-19 15:27:33 -08:00
Protobuf Team Bot
20b2cd7623 Use StandardCharsets.UTF_8 instead of an Internal.UTF_8 variable.
This is a functional no-op but has fringe benefits.

Having having the constant in the Internal class makes it a static init property which has some small but undesirable implications. The specific prompt of making this change is that it was discovered that when appreduce is inline-eliminating an Internal.checkNotNull it still needs to emit the bytecode ensure that this static init is run, as the static init is an observable side effect of calling that method. The only actual side effect that this could have is if these threw an exception, which would actually be _extremely bad_ if it ever actually happened in reality, but never will throw with these particular string literals that we use, so slightly bloating bytecode just to maintain a "would break everything if its not a no-op" feels bad.

StandardCharsets was added in Java 7, and so this was not available to use when this design was initially set up.

PiperOrigin-RevId: 872430237
2026-02-19 09:18:01 -08:00
Joshua Haberman
1816758069 Dropped support for Bazel 7.
This was previously announced here: https://protobuf.dev/support/migration/#dropped-bazel-7-support

#test-continuous

PiperOrigin-RevId: 870256373
2026-02-14 14:50:17 -08:00
Protobuf Team Bot
e3e3e5fe56 Fix CodedInputStream streaming fixed-size reads for very small buffers
StreamDecoder now uses a minimum buffer size of 8 to ensure all fixed-width reads can be handled using the buffer. New tests are added to verify this behavior.

While here, create a test-only variant of ByteArrayInputStream that retains the same semantics as InputStream.read(byte[],int,int) for better test fidelity.

PiperOrigin-RevId: 868144899
2026-02-10 07:53:57 -08:00
Tobias Werth
71eef33ed0 Automated Code Change
PiperOrigin-RevId: 868019237
2026-02-10 01:55:09 -08:00
Protobuf Team Bot
451f1e9723 Introduce a ByteString.substringNoCopy() method.
PiperOrigin-RevId: 866483595
2026-02-06 09:02:34 -08:00
Protobuf Team Bot
9b00a7be8f Switch to use Utf8.isValidUtf8() on the entire range we copy instead of first validating a subrange and then copying it to a dedicated byte[].
This close to an effective no-op today, but in future changes we should be able to be more efficient when we are validating an entire byte[] instead of only a subrange of one.

PiperOrigin-RevId: 866457946
2026-02-06 07:51:35 -08:00
Protobuf Team Bot
5a6e0a84d1 Improve comment on DynamicMessage memoized IsInitialized.
PiperOrigin-RevId: 865416143
2026-02-04 08:19:33 -08:00
Protobuf Team Bot
b159d053e9 Rollforward TextFormatEscaper optimization with bug fixed.
PiperOrigin-RevId: 864892608
2026-02-03 08:34:44 -08:00
Protobuf Team Bot
7c59b3c88e Memoize isInitialized result for DynamicMessage to improve parsing performance.
PiperOrigin-RevId: 864068668
2026-02-01 15:58:13 -08:00
Protobuf Team Bot
390820b62f Automated rollback of commit a73b06d153.
PiperOrigin-RevId: 862814816
2026-01-29 11:10:15 -08:00