Bigint.php 3.5 KB

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