aboutsummaryrefslogtreecommitdiff
path: root/jgvariant-ostree/src/main/java
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2023-12-10 09:20:48 +0100
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2023-12-10 09:22:48 +0100
commitaa11d82fe887d7c625c8bd0e89e2947e448d8bb4 (patch)
tree5080d40d3eb6d335916cfc8ef62403417236bcae /jgvariant-ostree/src/main/java
parent04a5ce11203665fe1f03547bcfb6618ec0915c38 (diff)
Add Decoder#encode.
Implements: - the encoding part of the GVariant specification - OSTree-specific encoding instructions for static deltas Untested. Change-Id: Idbfd6d7e92a9cdff7d8b138d0ecfa36d4f30eee4
Diffstat (limited to 'jgvariant-ostree/src/main/java')
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java3
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java3
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java2
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java19
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java56
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java52
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java45
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java3
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java13
-rw-r--r--jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java3
10 files changed, 179 insertions, 20 deletions
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java
index cfe3635..3bd8b25 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/ByteString.java
@@ -20,7 +20,8 @@ import org.jetbrains.annotations.Nullable;
*/
public record ByteString(byte[] bytes) {
- private static final Decoder<ByteString> DECODER = Decoder.ofByteArray().map(ByteString::new);
+ private static final Decoder<ByteString> DECODER =
+ Decoder.ofByteArray().map(ByteString::new, ByteString::bytes);
/**
* Returns a decoder for a {@code byte[]} that wraps the result in {@link ByteString}.
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java
index 261e2be..829664e 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Checksum.java
@@ -17,7 +17,8 @@ public record Checksum(ByteString byteString) {
private static final int SIZE = 32;
- private static final Decoder<Checksum> DECODER = ByteString.decoder().map(Checksum::new);
+ private static final Decoder<Checksum> DECODER =
+ ByteString.decoder().map(Checksum::new, Checksum::byteString);
public Checksum {
if (byteString.size() == 0) {
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java
index 57c8fc5..08e0b8c 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaFallback.java
@@ -25,7 +25,7 @@ public record DeltaFallback(
private static final Decoder<DeltaFallback> DECODER =
Decoder.ofStructure(
DeltaFallback.class,
- Decoder.ofByte().map(ObjectType::valueOf),
+ Decoder.ofByte().map(ObjectType::valueOf, ObjectType::byteValue),
Checksum.decoder(),
Decoder.ofLong(),
Decoder.ofLong());
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java
index 8c6fd19..2be0426 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaMetaEntry.java
@@ -5,6 +5,7 @@
package eu.mulk.jgvariant.ostree;
import eu.mulk.jgvariant.core.Decoder;
+import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
@@ -38,7 +39,9 @@ public record DeltaMetaEntry(
private static final Decoder<DeltaObject> DECODER =
Decoder.ofStructure(
- DeltaObject.class, Decoder.ofByte().map(ObjectType::valueOf), Checksum.decoder());
+ DeltaObject.class,
+ Decoder.ofByte().map(ObjectType::valueOf, ObjectType::byteValue),
+ Checksum.decoder());
/**
* Acquires a {@link Decoder} for the enclosing type.
@@ -57,7 +60,19 @@ public record DeltaMetaEntry(
Checksum.decoder(),
Decoder.ofLong(),
Decoder.ofLong(),
- Decoder.ofByteArray().map(DeltaMetaEntry::parseObjectList));
+ Decoder.ofByteArray()
+ .map(DeltaMetaEntry::parseObjectList, DeltaMetaEntry::serializeObjectList));
+
+ private static byte[] serializeObjectList(List<DeltaObject> deltaObjects) {
+ var output = new ByteArrayOutputStream();
+
+ for (var deltaObject : deltaObjects) {
+ output.write(deltaObject.objectType.byteValue());
+ output.writeBytes(deltaObject.checksum.byteString().bytes());
+ }
+
+ return output.toByteArray();
+ }
private static List<DeltaObject> parseObjectList(byte[] bytes) {
var byteBuffer = ByteBuffer.wrap(bytes);
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java
index 6edf217..d753a48 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaOperation.java
@@ -4,6 +4,7 @@
package eu.mulk.jgvariant.ostree;
+import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
/** An operation in a static delta. */
@@ -67,6 +68,42 @@ public sealed interface DeltaOperation {
};
}
+ default void writeTo(ByteArrayOutputStream output) {
+ if (this instanceof OpenSpliceAndCloseMeta openSpliceAndCloseMeta) {
+ output.write(DeltaOperationType.OPEN_SPLICE_AND_CLOSE.byteValue());
+ writeVarint64(output, openSpliceAndCloseMeta.offset);
+ writeVarint64(output, openSpliceAndCloseMeta.size);
+ } else if (this instanceof OpenSpliceAndCloseReal openSpliceAndCloseReal) {
+ output.write(DeltaOperationType.OPEN_SPLICE_AND_CLOSE.byteValue());
+ writeVarint64(output, openSpliceAndCloseReal.modeOffset);
+ writeVarint64(output, openSpliceAndCloseReal.xattrOffset);
+ writeVarint64(output, openSpliceAndCloseReal.size);
+ writeVarint64(output, openSpliceAndCloseReal.offset);
+ } else if (this instanceof Open open) {
+ output.write(DeltaOperationType.OPEN.byteValue());
+ writeVarint64(output, open.modeOffset);
+ writeVarint64(output, open.xattrOffset);
+ writeVarint64(output, open.size);
+ } else if (this instanceof Write write) {
+ output.write(DeltaOperationType.WRITE.byteValue());
+ writeVarint64(output, write.size);
+ writeVarint64(output, write.offset);
+ } else if (this instanceof SetReadSource setReadSource) {
+ output.write(DeltaOperationType.SET_READ_SOURCE.byteValue());
+ writeVarint64(output, setReadSource.offset);
+ } else if (this instanceof UnsetReadSource) {
+ output.write(DeltaOperationType.UNSET_READ_SOURCE.byteValue());
+ } else if (this instanceof Close) {
+ output.write(DeltaOperationType.CLOSE.byteValue());
+ } else if (this instanceof BsPatch bsPatch) {
+ output.write(DeltaOperationType.BSPATCH.byteValue());
+ writeVarint64(output, bsPatch.offset);
+ writeVarint64(output, bsPatch.size);
+ } else {
+ throw new IllegalStateException("unrecognized delta operation: %s".formatted(this));
+ }
+ }
+
/**
* Reads a Protobuf varint from a byte buffer.
*
@@ -86,4 +123,23 @@ public sealed interface DeltaOperation {
return acc;
}
+
+ /**
+ * Writes a Protobuf varint to an output stream.
+ *
+ * @see #readVarint64
+ */
+ private static void writeVarint64(ByteArrayOutputStream output, long value) {
+ while (value != 0) {
+ byte b = (byte) (value & 0x7F);
+ value >>= 7;
+ if (value != 0) {
+ b |= (byte) 0x80;
+ }
+ output.write(b);
+ if (value == 0) {
+ break;
+ }
+ }
+ }
}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java
index f89d414..31c192d 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaPartPayload.java
@@ -8,12 +8,14 @@ import eu.mulk.jgvariant.core.Decoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.channels.Channels;
import java.util.ArrayList;
import java.util.List;
+import org.tukaani.xz.LZMA2Options;
import org.tukaani.xz.XZInputStream;
+import org.tukaani.xz.XZOutputStream;
/**
* A payload file from a static delta.
@@ -36,7 +38,7 @@ public record DeltaPartPayload(
ByteString rawDataSource,
List<DeltaOperation> operations) {
- private static ByteBuffer preparse(ByteBuffer byteBuffer) {
+ private static ByteBuffer decompress(ByteBuffer byteBuffer) {
byte compressionByte = byteBuffer.get(0);
var dataSlice = byteBuffer.slice(1, byteBuffer.limit() - 1);
return switch (compressionByte) {
@@ -53,7 +55,7 @@ public record DeltaPartPayload(
yield ByteBuffer.wrap(decompressedOutputStream.toByteArray());
} catch (IOException e) {
// impossible
- throw new UncheckedIOException(e);
+ throw new IllegalStateException(e);
}
}
default -> throw new IllegalArgumentException(
@@ -61,10 +63,42 @@ public record DeltaPartPayload(
};
}
+ private static ByteBuffer compress(ByteBuffer dataSlice) {
+ var dataBytes = new byte[dataSlice.limit()];
+ dataSlice.get(dataBytes);
+ var compressedOutputStream = new ByteArrayOutputStream();
+
+ byte compressionByte = 'x';
+ compressedOutputStream.write(compressionByte);
+
+ try (var compressingOutputStream =
+ new XZOutputStream(compressedOutputStream, new LZMA2Options());
+ var compressingChannel = Channels.newChannel(compressingOutputStream)) {
+ compressingChannel.write(dataSlice);
+ compressingOutputStream.write(dataBytes);
+ } catch (IOException e) {
+ // impossible
+ throw new IllegalStateException(e);
+ }
+
+ var compressedBytes = compressedOutputStream.toByteArray();
+ return ByteBuffer.wrap(compressedBytes);
+ }
+
+ private static byte[] serializeDeltaOperationList(List<DeltaOperation> deltaOperations) {
+ var output = new ByteArrayOutputStream();
+
+ for (var currentOperation : deltaOperations) {
+ currentOperation.writeTo(output);
+ }
+
+ return output.toByteArray();
+ }
+
private static List<DeltaOperation> parseDeltaOperationList(
- ByteString byteString, List<ObjectType> objectTypes) {
+ byte[] bytes, List<ObjectType> objectTypes) {
List<DeltaOperation> deltaOperations = new ArrayList<>();
- var byteBuffer = ByteBuffer.wrap(byteString.bytes());
+ var byteBuffer = ByteBuffer.wrap(bytes);
int objectIndex = 0;
while (byteBuffer.hasRemaining()) {
@@ -119,8 +153,10 @@ public record DeltaPartPayload(
Decoder.ofArray(FileMode.decoder()),
Decoder.ofArray(Decoder.ofArray(Xattr.decoder())),
ByteString.decoder(),
- ByteString.decoder()
- .map(byteString -> parseDeltaOperationList(byteString, objectTypes)))
- .contramap(DeltaPartPayload::preparse);
+ Decoder.ofByteArray()
+ .map(
+ bytes -> parseDeltaOperationList(bytes, objectTypes),
+ deltaOperations -> serializeDeltaOperationList(deltaOperations)))
+ .contramap(DeltaPartPayload::decompress, DeltaPartPayload::compress);
}
}
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java
index 50da203..9513fa0 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/DeltaSuperblock.java
@@ -5,10 +5,16 @@
package eu.mulk.jgvariant.ostree;
import eu.mulk.jgvariant.core.Decoder;
+import eu.mulk.jgvariant.core.Signature;
+import eu.mulk.jgvariant.core.Variant;
+import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.text.ParseException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* A static delta.
@@ -67,10 +73,34 @@ public record DeltaSuperblock(
Checksum.decoder(),
Checksum.decoder(),
Commit.decoder(),
- Decoder.ofByteArray().map(DeltaSuperblock::parseDeltaNameList),
+ Decoder.ofByteArray()
+ .map(
+ DeltaSuperblock::parseDeltaNameList, DeltaSuperblock::serializeDeltaNameList),
Decoder.ofArray(DeltaMetaEntry.decoder()).withByteOrder(ByteOrder.LITTLE_ENDIAN),
Decoder.ofArray(DeltaFallback.decoder()).withByteOrder(ByteOrder.LITTLE_ENDIAN))
- .map(DeltaSuperblock::byteSwappedIfBigEndian);
+ .map(DeltaSuperblock::byteSwappedIfBigEndian, DeltaSuperblock::withSpecifiedByteOrder);
+
+ private DeltaSuperblock withSpecifiedByteOrder() {
+ Map<String, Variant> extendedMetadataMap = new HashMap<>(metadata().fields());
+
+ try {
+ extendedMetadataMap.putIfAbsent(
+ "ostree.endianness", new Variant(Signature.parse("y"), (byte) 'l'));
+ } catch (ParseException e) {
+ // impossible
+ throw new IllegalStateException(e);
+ }
+
+ return new DeltaSuperblock(
+ new Metadata(extendedMetadataMap),
+ timestamp,
+ fromChecksum,
+ toChecksum,
+ commit,
+ dependencies,
+ entries,
+ fallbacks);
+ }
private DeltaSuperblock byteSwappedIfBigEndian() {
// Fix up the endianness of the 'entries' and 'fallbacks' fields, which have
@@ -97,6 +127,17 @@ public record DeltaSuperblock(
fallbacks.stream().map(DeltaFallback::byteSwapped).toList());
}
+ private static byte[] serializeDeltaNameList(List<DeltaName> deltaNames) {
+ var output = new ByteArrayOutputStream();
+
+ for (var deltaName : deltaNames) {
+ output.writeBytes(deltaName.fromChecksum().byteString().bytes());
+ output.writeBytes(deltaName.toChecksum().byteString().bytes());
+ }
+
+ return output.toByteArray();
+ }
+
private static List<DeltaName> parseDeltaNameList(byte[] bytes) {
var byteBuffer = ByteBuffer.wrap(bytes);
List<DeltaName> deltaNames = new ArrayList<>();
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java
index 62f0331..f485be1 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/Metadata.java
@@ -20,7 +20,8 @@ import java.util.Map;
public record Metadata(Map<String, Variant> fields) {
private static final Decoder<Metadata> DECODER =
- Decoder.ofDictionary(Decoder.ofString(UTF_8), Decoder.ofVariant()).map(Metadata::new);
+ Decoder.ofDictionary(Decoder.ofString(UTF_8), Decoder.ofVariant())
+ .map(Metadata::new, Metadata::fields);
/**
* Acquires a {@link Decoder} for the enclosing type.
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java
index 827d5e4..1e1e58e 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SignedDelta.java
@@ -31,11 +31,18 @@ public record SignedDelta(
Decoder.ofStructure(
SignedDelta.class,
Decoder.ofLong().withByteOrder(ByteOrder.BIG_ENDIAN),
- ByteString.decoder().map(SignedDelta::decodeSuperblock),
+ Decoder.ofByteArray().map(SignedDelta::decodeSuperblock, SignedDelta::encodeSuperblock),
Decoder.ofDictionary(Decoder.ofString(US_ASCII), Decoder.ofVariant()));
- private static DeltaSuperblock decodeSuperblock(ByteString byteString) {
- return DeltaSuperblock.decoder().decode(ByteBuffer.wrap(byteString.bytes()));
+ private static DeltaSuperblock decodeSuperblock(byte[] bytes) {
+ return DeltaSuperblock.decoder().decode(ByteBuffer.wrap(bytes));
+ }
+
+ private static byte[] encodeSuperblock(DeltaSuperblock deltaSuperblock) {
+ var byteBuffer = DeltaSuperblock.decoder().encode(deltaSuperblock);
+ byte[] bytes = new byte[byteBuffer.remaining()];
+ byteBuffer.get(bytes);
+ return bytes;
}
/**
diff --git a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java
index 3c88759..5834c91 100644
--- a/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java
+++ b/jgvariant-ostree/src/main/java/eu/mulk/jgvariant/ostree/SummarySignature.java
@@ -22,7 +22,8 @@ import java.util.Map;
public record SummarySignature(Map<String, Variant> signatures) {
private static final Decoder<SummarySignature> DECODER =
- Decoder.ofDictionary(Decoder.ofString(UTF_8), Decoder.ofVariant()).map(SummarySignature::new);
+ Decoder.ofDictionary(Decoder.ofString(UTF_8), Decoder.ofVariant())
+ .map(SummarySignature::new, SummarySignature::signatures);
/**
* Acquires a {@link Decoder} for the enclosing type.