( workInProgress: Fiber, totalChildren: number, index: number, )
| 132 | } |
| 133 | |
| 134 | export function pushTreeId( |
| 135 | workInProgress: Fiber, |
| 136 | totalChildren: number, |
| 137 | index: number, |
| 138 | ) { |
| 139 | warnIfNotHydrating(); |
| 140 | |
| 141 | idStack[idStackIndex++] = treeContextId; |
| 142 | idStack[idStackIndex++] = treeContextOverflow; |
| 143 | idStack[idStackIndex++] = treeContextProvider; |
| 144 | |
| 145 | treeContextProvider = workInProgress; |
| 146 | |
| 147 | const baseIdWithLeadingBit = treeContextId; |
| 148 | const baseOverflow = treeContextOverflow; |
| 149 | |
| 150 | // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part |
| 151 | // of the id; we use it to account for leading 0s. |
| 152 | const baseLength = getBitLength(baseIdWithLeadingBit) - 1; |
| 153 | const baseId = baseIdWithLeadingBit & ~(1 << baseLength); |
| 154 | |
| 155 | const slot = index + 1; |
| 156 | const length = getBitLength(totalChildren) + baseLength; |
| 157 | |
| 158 | // 30 is the max length we can store without overflowing, taking into |
| 159 | // consideration the leading 1 we use to mark the end of the sequence. |
| 160 | if (length > 30) { |
| 161 | // We overflowed the bitwise-safe range. Fall back to slower algorithm. |
| 162 | // This branch assumes the length of the base id is greater than 5; it won't |
| 163 | // work for smaller ids, because you need 5 bits per character. |
| 164 | // |
| 165 | // We encode the id in multiple steps: first the base id, then the |
| 166 | // remaining digits. |
| 167 | // |
| 168 | // Each 5 bit sequence corresponds to a single base 32 character. So for |
| 169 | // example, if the current id is 23 bits long, we can convert 20 of those |
| 170 | // bits into a string of 4 characters, with 3 bits left over. |
| 171 | // |
| 172 | // First calculate how many bits in the base id represent a complete |
| 173 | // sequence of characters. |
| 174 | const numberOfOverflowBits = baseLength - (baseLength % 5); |
| 175 | |
| 176 | // Then create a bitmask that selects only those bits. |
| 177 | const newOverflowBits = (1 << numberOfOverflowBits) - 1; |
| 178 | |
| 179 | // Select the bits, and convert them to a base 32 string. |
| 180 | const newOverflow = (baseId & newOverflowBits).toString(32); |
| 181 | |
| 182 | // Now we can remove those bits from the base id. |
| 183 | const restOfBaseId = baseId >> numberOfOverflowBits; |
| 184 | const restOfBaseLength = baseLength - numberOfOverflowBits; |
| 185 | |
| 186 | // Finally, encode the rest of the bits using the normal algorithm. Because |
| 187 | // we made more room, this time it won't overflow. |
| 188 | const restOfLength = getBitLength(totalChildren) + restOfBaseLength; |
| 189 | const restOfNewBits = slot << restOfBaseLength; |
| 190 | const id = restOfNewBits | restOfBaseId; |
| 191 | const overflow = newOverflow + baseOverflow; |
no test coverage detected