Bigint.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <?php
  2. declare(strict_types=1);
  3. namespace ZipStream;
  4. use OverflowException;
  5. class Bigint
  6. {
  7. /**
  8. * @var int[]
  9. */
  10. private $bytes = [0, 0, 0, 0, 0, 0, 0, 0];
  11. /**
  12. * Initialize the bytes array
  13. *
  14. * @param int $value
  15. */
  16. public function __construct(int $value = 0)
  17. {
  18. $this->fillBytes($value, 0, 8);
  19. }
  20. /**
  21. * Fill the bytes field with int
  22. *
  23. * @param int $value
  24. * @param int $start
  25. * @param int $count
  26. * @return void
  27. */
  28. protected function fillBytes(int $value, int $start, int $count): void
  29. {
  30. for ($i = 0; $i < $count; $i++) {
  31. $this->bytes[$start + $i] = $i >= PHP_INT_SIZE ? 0 : $value & 0xFF;
  32. $value >>= 8;
  33. }
  34. }
  35. /**
  36. * Get an instance
  37. *
  38. * @param int $value
  39. * @return Bigint
  40. */
  41. public static function init(int $value = 0): self
  42. {
  43. return new self($value);
  44. }
  45. /**
  46. * Fill bytes from low to high
  47. *
  48. * @param int $low
  49. * @param int $high
  50. * @return Bigint
  51. */
  52. public static function fromLowHigh(int $low, int $high): self
  53. {
  54. $bigint = new Bigint();
  55. $bigint->fillBytes($low, 0, 4);
  56. $bigint->fillBytes($high, 4, 4);
  57. return $bigint;
  58. }
  59. /**
  60. * Get high 32
  61. *
  62. * @return int
  63. */
  64. public function getHigh32(): int
  65. {
  66. return $this->getValue(4, 4);
  67. }
  68. /**
  69. * Get value from bytes array
  70. *
  71. * @param int $end
  72. * @param int $length
  73. * @return int
  74. */
  75. public function getValue(int $end = 0, int $length = 8): int
  76. {
  77. $result = 0;
  78. for ($i = $end + $length - 1; $i >= $end; $i--) {
  79. $result <<= 8;
  80. $result |= $this->bytes[$i];
  81. }
  82. return $result;
  83. }
  84. /**
  85. * Get low FF
  86. *
  87. * @param bool $force
  88. * @return float
  89. */
  90. public function getLowFF(bool $force = false): float
  91. {
  92. if ($force || $this->isOver32()) {
  93. return (float)0xFFFFFFFF;
  94. }
  95. return (float)$this->getLow32();
  96. }
  97. /**
  98. * Check if is over 32
  99. *
  100. * @param bool $force
  101. * @return bool
  102. */
  103. public function isOver32(bool $force = false): bool
  104. {
  105. // value 0xFFFFFFFF already needs a Zip64 header
  106. return $force ||
  107. max(array_slice($this->bytes, 4, 4)) > 0 ||
  108. min(array_slice($this->bytes, 0, 4)) === 0xFF;
  109. }
  110. /**
  111. * Get low 32
  112. *
  113. * @return int
  114. */
  115. public function getLow32(): int
  116. {
  117. return $this->getValue(0, 4);
  118. }
  119. /**
  120. * Get hexadecimal
  121. *
  122. * @return string
  123. */
  124. public function getHex64(): string
  125. {
  126. $result = '0x';
  127. for ($i = 7; $i >= 0; $i--) {
  128. $result .= sprintf('%02X', $this->bytes[$i]);
  129. }
  130. return $result;
  131. }
  132. /**
  133. * Add
  134. *
  135. * @param Bigint $other
  136. * @return Bigint
  137. */
  138. public function add(Bigint $other): Bigint
  139. {
  140. $result = clone $this;
  141. $overflow = false;
  142. for ($i = 0; $i < 8; $i++) {
  143. $result->bytes[$i] += $other->bytes[$i];
  144. if ($overflow) {
  145. $result->bytes[$i]++;
  146. $overflow = false;
  147. }
  148. if ($result->bytes[$i] & 0x100) {
  149. $overflow = true;
  150. $result->bytes[$i] &= 0xFF;
  151. }
  152. }
  153. if ($overflow) {
  154. throw new OverflowException;
  155. }
  156. return $result;
  157. }
  158. }