blob: bb31e50629323f7dacde35fee34aca7b59ce7f2a [file] [log] [blame]
Matthias Andreas Benkardb5d657a2022-02-03 21:14:30 +01001// SPDX-FileCopyrightText: © 2021 Matthias Andreas Benkard <code@mail.matthias.benkard.de>
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
Matthias Andreas Benkard05114642021-12-29 21:51:29 +01005package eu.mulk.jgvariant.ostree;
6
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +01007import java.io.ByteArrayOutputStream;
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +01008import java.nio.ByteBuffer;
Matthias Andreas Benkard05114642021-12-29 21:51:29 +01009
10/** An operation in a static delta. */
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010011public sealed interface DeltaOperation {
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010012
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010013 record OpenSpliceAndCloseMeta(long offset, long size) implements DeltaOperation {}
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010014
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010015 record OpenSpliceAndCloseReal(long offset, long size, long modeOffset, long xattrOffset)
16 implements DeltaOperation {}
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010017
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010018 record Open(long size, long modeOffset, long xattrOffset) implements DeltaOperation {}
Matthias Andreas Benkard05114642021-12-29 21:51:29 +010019
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010020 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 Benkard05114642021-12-29 21:51:29 +010068 };
69 }
Matthias Andreas Benkardc981cde2021-12-30 20:37:39 +010070
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010071 default void writeTo(ByteArrayOutputStream output) {
Matthias Andreas Benkardc442ebe2023-12-10 17:58:38 +010072 if (this instanceof OpenSpliceAndCloseReal openSpliceAndCloseReal) {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +010073 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 Benkardc442ebe2023-12-10 17:58:38 +010078 } 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 Benkardaa11d822023-12-10 09:20:48 +010082 } 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 Benkardc981cde2021-12-30 20:37:39 +0100107 /**
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 Benkardaa11d822023-12-10 09:20:48 +0100126
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 Benkard147a1c12023-12-10 20:54:33 +0100133 int n = 0;
Matthias Andreas Benkardc442ebe2023-12-10 17:58:38 +0100134 do {
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100135 byte b = (byte) (value & 0x7F);
136 value >>= 7;
137 if (value != 0) {
138 b |= (byte) 0x80;
139 }
140 output.write(b);
Matthias Andreas Benkard147a1c12023-12-10 20:54:33 +0100141 ++n;
142 } while (value != 0 && n < 10);
Matthias Andreas Benkardaa11d822023-12-10 09:20:48 +0100143 }
Matthias Andreas Benkard05114642021-12-29 21:51:29 +0100144}