Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git
<?php
require_once __DIR__ . '/BufferedReader.php';

class LooseObjects {
  private string $objPath;

  public function __construct( string $objPath ) {
    $this->objPath = $objPath;
  }

  public function getSize( string $sha ): int {
    $path = $this->getPath( $sha );
    $size = 0;

    if( \is_file( $path ) ) {
      foreach( $this->streamInflated( $path ) as $chunk ) {
        $parts = \explode( ' ', $chunk['head'] );

        $size = isset( $parts[1] )
          ? (int)$parts[1]
          : 0;

        break;
      }
    }

    return $size;
  }

  public function peek(
    string $sha,
    int $length = 255
  ): string {
    $path = $this->getPath( $sha );
    $buf  = '';

    if( \is_file( $path ) ) {
      foreach(
        $this->streamInflated( $path, 8192 ) as $chunk
      ) {
        $buf .= $chunk['body'];

        if( \strlen( $buf ) >= $length ) {
          break;
        }
      }
    }

    return \substr( $buf, 0, $length );
  }

  public function stream(
    string $sha,
    callable $callback
  ): bool {
    $found = false;

    foreach( $this->streamChunks( $sha ) as $chunk ) {
      $found = true;
      $callback( $chunk );
    }

    return $found;
  }

  public function streamChunks(
    string $sha
  ): Generator {
    $path = $this->getPath( $sha );

    if( \is_file( $path ) ) {
      foreach(
        $this->streamInflated( $path ) as $chunk
      ) {
        if( $chunk['body'] !== '' ) {
          yield $chunk['body'];
        }
      }
    }
  }

  private function getPath( string $sha ): string {
    return "{$this->objPath}/"
      . \substr( $sha, 0, 2 ) . "/"
      . \substr( $sha, 2 );
  }

  private function streamInflated(
    string $path,
    int $bufSz = 16384
  ): Generator {
    $reader = new BufferedReader( $path );
    $infl   = $reader->isOpen()
      ? \inflate_init( \ZLIB_ENCODING_DEFLATE )
      : false;

    if( $reader->isOpen() && $infl !== false ) {
      $found  = false;
      $buffer = '';

      while( !$reader->eof() ) {
        $chunk    = $reader->read( $bufSz );
        $inflated = \inflate_add( $infl, $chunk );

        if( $inflated === false ) {
          break;
        }

        if( !$found ) {
          $buffer .= $inflated;
          $eos     = \strpos( $buffer, "\0" );

          if( $eos !== false ) {
            $found = true;

            yield [
              'head' => \substr(
                $buffer, 0, $eos
              ),
              'body' => \substr(
                $buffer, $eos + 1
              )
            ];
          }
        } elseif( $inflated !== '' ) {
          yield [
            'head' => '',
            'body' => $inflated
          ];
        }
      }
    }
  }
}