1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.data.protobuf;
|
---|
3 |
|
---|
4 | import java.util.Arrays;
|
---|
5 |
|
---|
6 | /**
|
---|
7 | * Parse packed values (only numerical values)
|
---|
8 | *
|
---|
9 | * @author Taylor Smock
|
---|
10 | * @since 17862
|
---|
11 | */
|
---|
12 | public class ProtobufPacked {
|
---|
13 | private final byte[] bytes;
|
---|
14 | private final long[] numbers;
|
---|
15 | private int location;
|
---|
16 |
|
---|
17 | /**
|
---|
18 | * Create a new ProtobufPacked object
|
---|
19 | *
|
---|
20 | * @param bytes The packed bytes
|
---|
21 | * @since 18695
|
---|
22 | */
|
---|
23 | public ProtobufPacked(byte[] bytes) {
|
---|
24 | this.location = 0;
|
---|
25 | this.bytes = bytes;
|
---|
26 |
|
---|
27 | // By creating a list of size bytes.length, we avoid 36 MB of allocations from list growth. This initialization
|
---|
28 | // only adds 3.7 MB to the ArrayList#init calls. Note that the real-world test case (Mapillary vector tiles)
|
---|
29 | // primarily created Shorts.
|
---|
30 | long[] numbersT = new long[bytes.length];
|
---|
31 | int index = 0;
|
---|
32 | // By reusing a ByteArrayOutputStream, we can reduce allocations in nextVarInt from 230 MB to 74 MB.
|
---|
33 | while (this.location < bytes.length) {
|
---|
34 | int start = this.location;
|
---|
35 | numbersT[index] = ProtobufParser.convertByteArray(this.bytes, ProtobufParser.VAR_INT_BYTE_SIZE,
|
---|
36 | start, this.nextVarInt());
|
---|
37 | index++;
|
---|
38 | }
|
---|
39 |
|
---|
40 | if (numbersT.length == index) {
|
---|
41 | this.numbers = numbersT;
|
---|
42 | } else {
|
---|
43 | this.numbers = Arrays.copyOf(numbersT, index);
|
---|
44 | }
|
---|
45 | }
|
---|
46 |
|
---|
47 | /**
|
---|
48 | * Get the parsed number array
|
---|
49 | *
|
---|
50 | * @return The number array
|
---|
51 | */
|
---|
52 | public long[] getArray() {
|
---|
53 | return this.numbers;
|
---|
54 | }
|
---|
55 |
|
---|
56 | /**
|
---|
57 | * Gets the location where the next var int begins. Note: changes {@link ProtobufPacked#location}.
|
---|
58 | * @return The next varint location
|
---|
59 | */
|
---|
60 | private int nextVarInt() {
|
---|
61 | while ((this.bytes[this.location] & ProtobufParser.MOST_SIGNIFICANT_BYTE)
|
---|
62 | == ProtobufParser.MOST_SIGNIFICANT_BYTE) {
|
---|
63 | // Get rid of the leading bit (shift left 1, then shift right 1 unsigned)
|
---|
64 | this.bytes[this.location] = (byte) (this.bytes[this.location] ^ ProtobufParser.MOST_SIGNIFICANT_BYTE);
|
---|
65 | this.location++;
|
---|
66 | }
|
---|
67 | return ++this.location;
|
---|
68 | }
|
---|
69 | }
|
---|