diff options
Diffstat (limited to 'jgvariant-ostree/src/main')
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. | 
