Matthias Andreas Benkard | b5d657a | 2022-02-03 21:14:30 +0100 | [diff] [blame] | 1 | // SPDX-FileCopyrightText: © 2021 Matthias Andreas Benkard <code@mail.matthias.benkard.de> |
| 2 | // |
| 3 | // SPDX-License-Identifier: LGPL-3.0-or-later |
| 4 | |
Matthias Andreas Benkard | 0511464 | 2021-12-29 21:51:29 +0100 | [diff] [blame] | 5 | package eu.mulk.jgvariant.ostree; |
| 6 | |
Matthias Andreas Benkard | aa11d82 | 2023-12-10 09:20:48 +0100 | [diff] [blame] | 7 | import java.io.ByteArrayOutputStream; |
Matthias Andreas Benkard | c981cde | 2021-12-30 20:37:39 +0100 | [diff] [blame] | 8 | import java.nio.ByteBuffer; |
Matthias Andreas Benkard | 0511464 | 2021-12-29 21:51:29 +0100 | [diff] [blame] | 9 | |
| 10 | /** An operation in a static delta. */ |
Matthias Andreas Benkard | c981cde | 2021-12-30 20:37:39 +0100 | [diff] [blame] | 11 | public sealed interface DeltaOperation { |
Matthias Andreas Benkard | 0511464 | 2021-12-29 21:51:29 +0100 | [diff] [blame] | 12 | |
Matthias Andreas Benkard | c981cde | 2021-12-30 20:37:39 +0100 | [diff] [blame] | 13 | record OpenSpliceAndCloseMeta(long offset, long size) implements DeltaOperation {} |
Matthias Andreas Benkard | 0511464 | 2021-12-29 21:51:29 +0100 | [diff] [blame] | 14 | |
Matthias Andreas Benkard | c981cde | 2021-12-30 20:37:39 +0100 | [diff] [blame] | 15 | record OpenSpliceAndCloseReal(long offset, long size, long modeOffset, long xattrOffset) |
| 16 | implements DeltaOperation {} |
Matthias Andreas Benkard | 0511464 | 2021-12-29 21:51:29 +0100 | [diff] [blame] | 17 | |
Matthias Andreas Benkard | c981cde | 2021-12-30 20:37:39 +0100 | [diff] [blame] | 18 | record Open(long size, long modeOffset, long xattrOffset) implements DeltaOperation {} |
Matthias Andreas Benkard | 0511464 | 2021-12-29 21:51:29 +0100 | [diff] [blame] | 19 | |
Matthias Andreas Benkard | c981cde | 2021-12-30 20:37:39 +0100 | [diff] [blame] | 20 | record Write(long offset, long size) implements DeltaOperation {} |
| 21 | |
| 22 | record SetReadSource(long offset) implements DeltaOperation {} |
| 23 | |
| 24 | record UnsetReadSource() implements DeltaOperation {} |
| 25 | |
| 26 | record Close() implements DeltaOperation {} |
| 27 | |
| 28 | record BsPatch(long offset, long size) implements DeltaOperation {} |
| 29 | |
| 30 | static DeltaOperation readFrom(ByteBuffer byteBuffer, ObjectType objectType) { |
| 31 | byte opcode = byteBuffer.get(); |
| 32 | return switch (DeltaOperationType.valueOf(opcode)) { |
| 33 | case OPEN_SPLICE_AND_CLOSE -> { |
| 34 | if (objectType == ObjectType.FILE || objectType == ObjectType.PAYLOAD_LINK) { |
| 35 | long modeOffset = readVarint64(byteBuffer); |
| 36 | long xattrOffset = readVarint64(byteBuffer); |
| 37 | long size = readVarint64(byteBuffer); |
| 38 | long offset = readVarint64(byteBuffer); |
| 39 | yield new OpenSpliceAndCloseReal(offset, size, modeOffset, xattrOffset); |
| 40 | } else { |
| 41 | long size = readVarint64(byteBuffer); |
| 42 | long offset = readVarint64(byteBuffer); |
| 43 | yield new OpenSpliceAndCloseMeta(offset, size); |
| 44 | } |
| 45 | } |
| 46 | case OPEN -> { |
| 47 | long modeOffset = readVarint64(byteBuffer); |
| 48 | long xattrOffset = readVarint64(byteBuffer); |
| 49 | long size = readVarint64(byteBuffer); |
| 50 | yield new Open(size, modeOffset, xattrOffset); |
| 51 | } |
| 52 | case WRITE -> { |
| 53 | long size = readVarint64(byteBuffer); |
| 54 | long offset = readVarint64(byteBuffer); |
| 55 | yield new Write(offset, size); |
| 56 | } |
| 57 | case SET_READ_SOURCE -> { |
| 58 | long offset = readVarint64(byteBuffer); |
| 59 | yield new SetReadSource(offset); |
| 60 | } |
| 61 | case UNSET_READ_SOURCE -> new UnsetReadSource(); |
| 62 | case CLOSE -> new Close(); |
| 63 | case BSPATCH -> { |
| 64 | long offset = readVarint64(byteBuffer); |
| 65 | long size = readVarint64(byteBuffer); |
| 66 | yield new BsPatch(offset, size); |
| 67 | } |
Matthias Andreas Benkard | 0511464 | 2021-12-29 21:51:29 +0100 | [diff] [blame] | 68 | }; |
| 69 | } |
Matthias Andreas Benkard | c981cde | 2021-12-30 20:37:39 +0100 | [diff] [blame] | 70 | |
Matthias Andreas Benkard | aa11d82 | 2023-12-10 09:20:48 +0100 | [diff] [blame] | 71 | default void writeTo(ByteArrayOutputStream output) { |
Matthias Andreas Benkard | c442ebe | 2023-12-10 17:58:38 +0100 | [diff] [blame] | 72 | if (this instanceof OpenSpliceAndCloseReal openSpliceAndCloseReal) { |
Matthias Andreas Benkard | aa11d82 | 2023-12-10 09:20:48 +0100 | [diff] [blame] | 73 | output.write(DeltaOperationType.OPEN_SPLICE_AND_CLOSE.byteValue()); |
| 74 | writeVarint64(output, openSpliceAndCloseReal.modeOffset); |
| 75 | writeVarint64(output, openSpliceAndCloseReal.xattrOffset); |
| 76 | writeVarint64(output, openSpliceAndCloseReal.size); |
| 77 | writeVarint64(output, openSpliceAndCloseReal.offset); |
Matthias Andreas Benkard | c442ebe | 2023-12-10 17:58:38 +0100 | [diff] [blame] | 78 | } else if (this instanceof OpenSpliceAndCloseMeta openSpliceAndCloseMeta) { |
| 79 | output.write(DeltaOperationType.OPEN_SPLICE_AND_CLOSE.byteValue()); |
| 80 | writeVarint64(output, openSpliceAndCloseMeta.size); |
| 81 | writeVarint64(output, openSpliceAndCloseMeta.offset); |
Matthias Andreas Benkard | aa11d82 | 2023-12-10 09:20:48 +0100 | [diff] [blame] | 82 | } else if (this instanceof Open open) { |
| 83 | output.write(DeltaOperationType.OPEN.byteValue()); |
| 84 | writeVarint64(output, open.modeOffset); |
| 85 | writeVarint64(output, open.xattrOffset); |
| 86 | writeVarint64(output, open.size); |
| 87 | } else if (this instanceof Write write) { |
| 88 | output.write(DeltaOperationType.WRITE.byteValue()); |
| 89 | writeVarint64(output, write.size); |
| 90 | writeVarint64(output, write.offset); |
| 91 | } else if (this instanceof SetReadSource setReadSource) { |
| 92 | output.write(DeltaOperationType.SET_READ_SOURCE.byteValue()); |
| 93 | writeVarint64(output, setReadSource.offset); |
| 94 | } else if (this instanceof UnsetReadSource) { |
| 95 | output.write(DeltaOperationType.UNSET_READ_SOURCE.byteValue()); |
| 96 | } else if (this instanceof Close) { |
| 97 | output.write(DeltaOperationType.CLOSE.byteValue()); |
| 98 | } else if (this instanceof BsPatch bsPatch) { |
| 99 | output.write(DeltaOperationType.BSPATCH.byteValue()); |
| 100 | writeVarint64(output, bsPatch.offset); |
| 101 | writeVarint64(output, bsPatch.size); |
| 102 | } else { |
| 103 | throw new IllegalStateException("unrecognized delta operation: %s".formatted(this)); |
| 104 | } |
| 105 | } |
| 106 | |
Matthias Andreas Benkard | c981cde | 2021-12-30 20:37:39 +0100 | [diff] [blame] | 107 | /** |
| 108 | * Reads a Protobuf varint from a byte buffer. |
| 109 | * |
| 110 | * <p>Varint64 encoding is little-endian and works by using the lower 7 bits of each byte as the |
| 111 | * payload and the 0x80 bit as an indicator of whether the varint continues. |
| 112 | */ |
| 113 | private static long readVarint64(ByteBuffer byteBuffer) { |
| 114 | long acc = 0L; |
| 115 | |
| 116 | for (int i = 0; i < 10; ++i) { |
| 117 | long b = byteBuffer.get(); |
| 118 | acc |= (b & 0x7F) << (i * 7); |
| 119 | if ((b & 0x80) == 0) { |
| 120 | break; |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | return acc; |
| 125 | } |
Matthias Andreas Benkard | aa11d82 | 2023-12-10 09:20:48 +0100 | [diff] [blame] | 126 | |
| 127 | /** |
| 128 | * Writes a Protobuf varint to an output stream. |
| 129 | * |
| 130 | * @see #readVarint64 |
| 131 | */ |
| 132 | private static void writeVarint64(ByteArrayOutputStream output, long value) { |
Matthias Andreas Benkard | 147a1c1 | 2023-12-10 20:54:33 +0100 | [diff] [blame^] | 133 | int n = 0; |
Matthias Andreas Benkard | c442ebe | 2023-12-10 17:58:38 +0100 | [diff] [blame] | 134 | do { |
Matthias Andreas Benkard | aa11d82 | 2023-12-10 09:20:48 +0100 | [diff] [blame] | 135 | byte b = (byte) (value & 0x7F); |
| 136 | value >>= 7; |
| 137 | if (value != 0) { |
| 138 | b |= (byte) 0x80; |
| 139 | } |
| 140 | output.write(b); |
Matthias Andreas Benkard | 147a1c1 | 2023-12-10 20:54:33 +0100 | [diff] [blame^] | 141 | ++n; |
| 142 | } while (value != 0 && n < 10); |
Matthias Andreas Benkard | aa11d82 | 2023-12-10 09:20:48 +0100 | [diff] [blame] | 143 | } |
Matthias Andreas Benkard | 0511464 | 2021-12-29 21:51:29 +0100 | [diff] [blame] | 144 | } |