All Downloads are FREE. Search and download functionalities are using the official Maven repository.

br.com.objectos.git.WriteTree Maven / Gradle / Ivy

/*
 * Copyright (C) 2020-2022 Objectos Software LTDA.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package br.com.objectos.git;

import br.com.objectos.collections.list.ImmutableList;
import br.com.objectos.core.throwable.Try;
import br.com.objectos.fs.Directory;
import br.com.objectos.fs.NotRegularFileException;
import br.com.objectos.fs.PathNameVisitor;
import br.com.objectos.fs.RegularFile;
import br.com.objectos.fs.ResolvedPath;
import br.com.objectos.logging.Event2;
import br.com.objectos.logging.Events;
import br.com.objectos.logging.TypeHint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.zip.Deflater;

final class WriteTree extends AbstractGitEngineTask {

  static final Event2, MutableTree> ESTART;

  static final Event2, ObjectId> ESUCCESS;

  private static final byte _ASSEMBLE = 1;

  private static final byte _RESULT = 2;

  private static final byte IO_WRITE = 1;

  private static final byte REND = 1;

  private static final byte RIMMUTABLE = 2;

  private static final byte RMUTABLE = 3;

  static {
    Class s;
    s = WriteTree.class;

    TypeHint> gitCommand = TypeHint.> get();

    ESTART = Events.debug(s, "START", gitCommand, TypeHint.of(MutableTree.class));

    ESUCCESS = Events.debug(s, "SUCCESS", gitCommand, TypeHint.of(ObjectId.class));
  }

  private ByteBuffer byteBuffer;

  private Charset charset;

  private IntStack dataStack;

  private ByteArrayWriter dataTape;

  private Deflater deflater;

  private MessageDigest messageDigest;

  private ByteArrayWriter objectContents;

  private ObjectId objectId;

  private final byte[] objectIdBytes = new byte[ObjectId.BYTE_LENGTH];

  private int objectSize;

  private Repository repository;

  private StringBuilder stringBuilder;

  private MutableTree tree;

  private ByteArrayWriter treeBody;

  WriteTree(GitInjector injector) {
    super(injector);
  }

  public final void setInput(GitCommand command, Repository repository, MutableTree tree) {
    checkSetInput();

    this.command = command;

    this.repository = repository;

    this.tree = tree;
  }

  @Override
  final byte execute(byte state) {
    switch (state) {
      case _ASSEMBLE:
        return executeAssemble();
      case _RESULT:
        return executeResult();
      default:
        return super.execute(state);
    }
  }

  @Override
  final byte executeFinally() {
    byteBuffer = null;

    charset = null;

    dataStack = injector.putIntStack(dataStack);

    dataTape = injector.putByteArrayWriter(dataTape);

    deflater = injector.putDeflater(deflater);

    messageDigest = injector.putMessageDigest(messageDigest);

    objectContents = injector.putByteArrayWriter(objectContents);

    objectId = null;

    Arrays.fill(objectIdBytes, (byte) 0);

    objectSize = 0;

    repository = null;

    stringBuilder = injector.putStringBuilder(stringBuilder);

    tree = null;

    treeBody = injector.putByteArrayWriter(treeBody);

    return super.executeFinally();
  }

  @Override
  final void executeIo(byte task) throws IOException {
    switch (task) {
      case IO_WRITE:
        ioWrite();
        break;
      default:
        super.executeIo(task);
        break;
    }
  }

  @Override
  final byte executeStart() {
    super.executeStart();

    stringBuilder = injector.getStringBuilder();

    IllegalArgumentException maybe;
    maybe = tree.validate(stringBuilder);

    if (maybe != null) {
      return toError(maybe);
    } else {
      charset = repository.getCharset();

      dataTape = injector.getByteArrayWriter();

      dataStack = injector.getIntStack();

      deflater = injector.getDeflater();

      messageDigest = injector.getMessageDigest(Git.SHA1);

      objectContents = injector.getByteArrayWriter();

      treeBody = injector.getByteArrayWriter();

      log(ESTART, command, tree);

      return _ASSEMBLE;
    }
  }

  final void skipObject() {
    byteBuffer.position(
        byteBuffer.position() + objectSize
    );
  }

  final void write(ResolvedPath notFound) throws IOException {
    notFound.createParents();

    Throwable rethrow;
    rethrow = Try.begin();

    FileChannel channel;
    channel = notFound.openWriteChannel();

    int limit;
    limit = byteBuffer.limit();

    try {
      byteBuffer.limit(byteBuffer.position() + objectSize);

      while (byteBuffer.hasRemaining()) {
        channel.write(byteBuffer);
      }
    } catch (Throwable e) {
      rethrow = e;
    } finally {
      rethrow = Try.close(rethrow, channel);
    }

    byteBuffer.limit(limit);

    Try.rethrowIfPossible(rethrow, IOException.class);
  }

  final void writeImmutable(ObjectId object) {
    dataTape.write(RIMMUTABLE);

    dataStack.push(dataTape.size());

    dataTape.write(object.getBytes());
  }

  final void writeMutableBlob(byte[] contents) {
    writeMutable0(ObjectKind.BLOB, contents.length);

    objectContents.write(contents);

    writeMutable1();
  }

  final void writeMutableTree(ImmutableList sortedEntries) {
    treeBody.clear();

    for (int i = 0, size = sortedEntries.size(); i < size; i++) {
      MutableTreeEntry entry;
      entry = sortedEntries.get(i);

      EntryMode mode;
      mode = entry.getMode();

      String name;
      name = entry.getName();

      treeBody.write(mode.getBytes());

      treeBody.write(Git.UTF8__space);

      byte[] nameBytes;
      nameBytes = name.getBytes(charset);

      treeBody.write(nameBytes);

      treeBody.write(Git.NULL);

      int offset;
      offset = dataStack.pop();

      treeBody.write(dataTape, offset, ObjectId.BYTE_LENGTH);
    }

    writeMutable0(ObjectKind.TREE, treeBody.size());

    objectContents.write(treeBody);

    writeMutable1();
  }

  private byte executeAssemble() {
    tree.executeWriteTree(this);

    dataTape.write(REND);

    byteBuffer = dataTape.asByteBuffer();

    return toIo(IO_WRITE, _RESULT, _FINALLY);
  }

  private byte executeResult() {
    setResult(objectId);

    log(ESUCCESS, command, objectId);

    return _FINALLY;
  }

  private void ioWrite() throws IOException {
    while (byteBuffer.hasRemaining()) {
      byte code;
      code = byteBuffer.get();

      switch (code) {
        case REND:
          // noop
          break;
        case RIMMUTABLE:
          ioWriteImmutable();
          break;
        case RMUTABLE:
          ioWriteMutable();
          break;
        default:
          throw new AssertionError("Unexpected record type: code=" + code);
      }
    }
  }

  private void ioWriteImmutable() {
    byteBuffer.position(
        byteBuffer.position() + ObjectId.BYTE_LENGTH
    );
  }

  private void ioWriteMutable() throws IOException {
    byteBuffer.get(objectIdBytes);

    objectSize = byteBuffer.getInt();

    objectId = ObjectId.copyOf(objectIdBytes);

    ResolvedPath loose;
    loose = repository.resolveLooseObject(objectId);

    loose.acceptPathNameVisitor(IoWriteMutable.INSTANCE, this);
  }

  private void writeMutable0(ObjectKind kind, int size) {
    objectContents.clear();

    byte[] prefix;
    prefix = kind.headerPrefix;

    objectContents.write(prefix);

    String bodySize;
    bodySize = Integer.toString(size, 10);

    byte[] objectSizeBytes;
    objectSizeBytes = bodySize.getBytes(charset);

    objectContents.write(objectSizeBytes);

    objectContents.write(Git.NULL);
  }

  private void writeMutable1() {
    messageDigest.reset();

    objectContents.update(messageDigest);

    byte[] hash;
    hash = messageDigest.digest();

    dataTape.write(RMUTABLE);

    dataStack.push(dataTape.size());

    dataTape.write(hash);

    int offset;
    offset = dataTape.size();

    byte zero;
    zero = 0;

    dataTape.write(zero);
    dataTape.write(zero);
    dataTape.write(zero);
    dataTape.write(zero);

    int size;
    size = dataTape.size();

    objectContents.deflate(deflater, dataTape);

    size = dataTape.size() - size;

    dataTape.putInt(offset, size);
  }

  private enum IoWriteMutable implements PathNameVisitor {
    INSTANCE;

    @Override
    public final Void visitDirectory(Directory directory, WriteTree p) throws IOException {
      throw new NotRegularFileException(directory);
    }

    @Override
    public final Void visitNotFound(ResolvedPath notFound, WriteTree p) throws IOException {
      p.write(notFound);

      return null;
    }

    @Override
    public final Void visitRegularFile(RegularFile file, WriteTree p) throws IOException {
      p.skipObject();

      return null;
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy