1<?php
2
3declare(strict_types=1);
4
5/*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18namespace TYPO3\CMS\Core\Type;
19
20/**
21 * The BitSet class is a helper class to manage bit sets. It eases the work with bits and bitwise
22 * operations by providing a reliable and tested API.
23 *
24 * The class can be used standalone or as a parent for more verbose classes that handle bit sets.
25 *
26 *
27 * The functionality is best described by an example:
28 *
29 * define('PERMISSIONS_NONE', 0b0); // 0
30 * define('PERMISSIONS_PAGE_SHOW', 0b1); // 1
31 * define('PERMISSIONS_PAGE_EDIT', 0b10); // 2
32 * define('PERMISSIONS_PAGE_DELETE', 0b100); // 4
33 *
34 * $bitSet = new \TYPO3\CMS\Core\Type\BitSet(PERMISSIONS_PAGE_SHOW | PERMISSIONS_PAGE_EDIT);
35 * $bitSet->get(PERMISSIONS_PAGE_SHOW); // true
36 * $bitSet->get(PERMISSIONS_PAGE_DELETE); // false
37 *
38 * Another example shows how to possibly extend the class:
39 *
40 * class Permissions extends \TYPO3\CMS\Core\Type\BitSet
41 * {
42 *     public const NONE = 0b0; // 0
43 *     public const PAGE_SHOW = 0b1; // 1
44 *
45 *     public function isGranted(int $permission): bool
46 *     {
47 *         return $this->get($permission);
48 *     }
49 *
50 *     public function grant(int $permission): void
51 *     {
52 *         $this->set($permission);
53 *     }
54 * }
55 *
56 * $permissions = new Permissions();
57 * $permissions->isGranted(Permissions::PAGE_SHOW); // false
58 * $permissions->grant(Permissions::PAGE_SHOW);
59 * $permissions->isGranted(Permissions::PAGE_SHOW); // true
60 */
61class BitSet
62{
63    /**
64     * @var int
65     */
66    private $set;
67
68    /**
69     * @param int $set
70     */
71    public function __construct(int $set = 0)
72    {
73        $this->set = $set;
74    }
75
76    /**
77     * Performs the same operation as {@see or()} without the need to create a BitSet instance from
78     * an integer value.
79     *
80     * @param int $bitIndex
81     */
82    public function set(int $bitIndex): void
83    {
84        $this->set |= $bitIndex;
85    }
86
87    /**
88     * @param int $bitIndex
89     * @param bool $value
90     */
91    public function setValue(int $bitIndex, bool $value): void
92    {
93        if ($value) {
94            $this->set($bitIndex);
95        } else {
96            $this->unset($bitIndex);
97        }
98    }
99
100    /**
101     * Performs the same operation as {@see andNot()} without the need to create a BitSet instance from
102     * an integer value.
103     *
104     * @param int $bitIndex
105     */
106    public function unset(int $bitIndex): void
107    {
108        $this->set &= ~$bitIndex;
109    }
110
111    /**
112     * @param int $bitIndex
113     * @return bool
114     */
115    public function get(int $bitIndex): bool
116    {
117        return ($bitIndex & $this->set) === $bitIndex;
118    }
119
120    /**
121     * Sets all of the bits in this BitSet to false.
122     */
123    public function clear(): void
124    {
125        $this->set = 0;
126    }
127
128    /**
129     * Performs a logical AND of this target bit set with the argument bit set. This bit set is
130     * modified so that each bit in it has the value true if and only if it both initially had the
131     * value true and the corresponding bit in the bit set argument also had the value true.
132     *
133     * @param BitSet $set
134     */
135    public function and(BitSet $set): void
136    {
137        $this->set &= $set->__toInt();
138    }
139
140    /**
141     * Performs a logical OR of this bit set with the bit set argument. This bit set is modified so
142     * that a bit in it has the value true if and only if it either already had the value true or
143     * the corresponding bit in the bit set argument has the value true.
144     *
145     * @param BitSet $set
146     */
147    public function or(BitSet $set): void
148    {
149        $this->set |= $set->__toInt();
150    }
151
152    /**
153     * Performs a logical XOR of this bit set with the bit set argument. This bit set is modified so
154     * that a bit in it has the value true if and only if one of the following statements holds:
155     *
156     * - The bit initially has the value true, and the corresponding bit
157     *   in the argument has the value false.
158     * - The bit initially has the value false, and the corresponding bit
159     *   in the argument has the value true.
160     *
161     * @param BitSet $set
162     */
163    public function xor(BitSet $set): void
164    {
165        $this->set ^= $set->__toInt();
166    }
167
168    /**
169     * Clears all of the bits in this BitSet whose corresponding bit is set in the specified BitSet.
170     *
171     * @param BitSet $set
172     */
173    public function andNot(BitSet $set): void
174    {
175        $this->set &= ~$set->__toInt();
176    }
177
178    /**
179     * Returns the integer representation of the internal set.
180     * (As PHP does not know a byte type, the internal set is already handled as an integer and can
181     * therefore directly be returned)
182     *
183     * @return int
184     */
185    public function __toInt(): int
186    {
187        return $this->set;
188    }
189
190    /**
191     * Returns the (binary) string representation of the internal (integer) set.
192     *
193     * @return string
194     */
195    public function __toString(): string
196    {
197        return '0b' . decbin($this->set);
198    }
199}
200