content: package im.actor.runtime.crypto.primitives.streebog;
  
  // Disabling Bounds checks for speeding up calculations
  
  /*-[
  #define J2OBJC_DISABLE_ALL_CHECKS 1
  ]-*/
  
  public class StreebogDigest {
  
      private final int hashLength;
      private Int512 h = new Int512();
      private Int512 m = new Int512();
      private Int512 e = new Int512();
      private int pt;
      private long n;
  
      public StreebogDigest(int hashLength) {
          this.hashLength = hashLength;
          reset();
      }
  
      public void reset() {
          // IV:: 01010.. for 256-bit hash, 0000... for 512-bit hash
          // memset(&sbx->h, hlen == 32 ? 0x01 : 0x00, 64);
          // memset(&sbx->e, 0x00, 64);
          for (int i = 0; i < 64; i++) {
              if (hashLength == 32) {
                  h.setByte(i, (byte) 0x01);
              } else {
                  h.setByte(i, (byte) 0x00);
              }
              e.setByte(i, (byte) 0x00);
          }
  
          // sbx->pt = 63;
          // sbx->n = 0;
          pt = 63;
          n = 0;
      }
  
      public void update(byte[] in, int offset, int length) {
  
          // j = sbx->pt;
          int j = pt;
          for (int i = 0; i < length; i++) {
              m.setByte(j--, (byte) (in[offset + i] & 0xFF));
  
              // compress
              // if (j < 0) {
              if (j < 0) {
                  // streebog_g(&sbx->h, &sbx->m, sbx->n);
                  StreebogMath.streebog_g(h, m, n);
                  // sbx->n += 0x200;
                  n += 0x200;
  
                  // epsilon summation
                  // c = 0;
                  int c = 0;
  
                  // for (j = 63; j >= 0; j--) {
                  for (j = 63; j >= 0; j--) {
                      // c += sbx->e.b[j] + sbx->m.b[j];
                      c += (e.getByte(j) & 0xFF) + (m.getByte(j) & 0xFF);
                      // sbx->e.b[j] = c & 0xFF;
                      e.setByte(j, (byte) (c & 0xFF));
                      // c >>= 8;
                      c >>= 8;
                  }
  
                  // j = 63;
                  j = 63;
              }
          }
  
          // sbx->pt = j;
          pt = j;
  
      }
  
      public void doFinal(byte[] out, int offset) {
          // pad the message and run final g
          // i = sbx->pt;
          int i = pt;
          // sbx->m.b[i--] = 0x01;
          m.setByte(i--, (byte) 0x01);
          while (i >= 0) {
              // sbx->m.b[i--] = 0x00;
              m.setByte(i--, (byte) 0x00);
          }
          // streebog_g(&sbx->h, &sbx->m, sbx->n);
          StreebogMath.streebog_g(h, m, n);
  
          // epsilon summation
          int c = 0;
          for (i = 63; i >= 0; i--) {
              // c += sbx->e.b[i] + sbx->m.b[i];
              c += (e.getByte(i) & 0xFF) + (m.getByte(i) & 0xFF);
              // sbx->e.b[i] = c & 0xFF;
              e.setByte(i, (byte) (c & 0xFF));
              // c >>= 8;
              c = c >> 8;
          }
  
          // finalization n
          // memset(&sbx->m, 0x00, 64);
          for (int j = 0; j < 64; j++) {
              m.setByte(j, (byte) 0x00);
          }
  
          // sbx->n += (63 - sbx->pt) << 3;      // total bits
          n += (63 - pt) << 3;
          for (i = 63; n > 0; i--) {
              // sbx->m.b[i] = sbx->n & 0xFF;
              m.setByte(i, (byte) (n & 0xFF));
              // sbx->n >>= 8;
              n = n >> 8;
          }
  
          // streebog_g(&sbx->h, &sbx->m, 0);
          // streebog_g(&sbx->h, &sbx->e, 0);
          StreebogMath.streebog_g(h, m, 0);
          StreebogMath.streebog_g(h, e, 0);
  
          // copy the result
          // memcpy(hash, &sbx->h, sbx->hlen);
          for (int j = 0; j < hashLength; j++) {
              out[offset + j] = h.getByte(j);
          }
  
          // clear out sensitive stuff
          reset();
      }
  }
  