Понимание установлен ли бит в определенной позиции двоичного числа (репрезентации целого числа в двоичной системе) является фундаментальной задачей для разработчиков работающих с данными на низком уровне.
Простой пример: у нас есть число 7
, в двоичной системе оно будет выглядеть как 111
и т.д.:
num_to_binary = lambda n: bin(n).replace('0b', '')
print(num_to_binary(7)) # 111
print(num_to_binary(10)) # 1010
print(num_to_binary(32)) # 100000
Теперь сформулируем практическую задачу: предположим мы работаем с API Wildberries и мы получили некий JWT Token внутри payload которого зашифрованы некоторые данные, допустим в свойстве s
лежит число в котором по битовой маске зашифрованы права доступа к разным разделам API и мы хотим определить позицию бита с целью дешифровки прав которые предоставляет нам токен.
Значения бит (взято из документации WB) выглядят следующим образом:
Позиция бита |
Свойство (если бит равен 1) |
1 |
Доступ к категории Контент |
2 |
Доступ к категории Аналитика |
3 |
Доступ к категории Цены и скидки |
4 |
Доступ к категории Маркетплейс |
5 |
Доступ к категории Статистика |
6 |
Доступ к категории Продвижение |
7 |
Доступ к категории Вопросы и отзывы |
8 |
Доступ к категории Рекомендации |
9 |
Доступ к категории Чат с покупателями |
30 |
Токен только на чтение |
Теперь получим токен и дешифруем его (самый простой способ jwt.io), нас интересует число в атрибуте токена s
.
Самый простой способ: использовать right shift (сдвиг вправо) c побитовым & (И)
is_bit_set_in_position = lambda n, pos: (n >> pos) & 1
def check_wb_token_rights(bit_mask):
print(f'For number: {bit_mask}')
print('Доступ к категории Контент', is_bit_set_in_position(bit_mask, 1))
print('Доступ к категории Аналитика', is_bit_set_in_position(bit_mask, 2))
print('Доступ к категории Цены и скидки', is_bit_set_in_position(bit_mask, 3))
print('Доступ к категории Маркетплейс', is_bit_set_in_position(bit_mask, 4))
print('Доступ к категории Статистика', is_bit_set_in_position(bit_mask, 5))
print('Доступ к категории Продвижение', is_bit_set_in_position(bit_mask, 6))
print('Доступ к категории Вопросы и отзывы', is_bit_set_in_position(bit_mask, 7))
print('Доступ к категории Рекомендации', is_bit_set_in_position(bit_mask, 8))
print('Доступ к категории Чат с покупателями', is_bit_set_in_position(bit_mask, 9))
print('Токен только на чтение', is_bit_set_in_position(bit_mask, 30))
print('\n')
check_wb_token_rights(2)
'''
For number: 2
Доступ к категории Контент 1
Доступ к категории Аналитика 0
Доступ к категории Цены и скидки 0
Доступ к категории Маркетплейс 0
Доступ к категории Статистика 0
Доступ к категории Продвижение 0
Доступ к категории Вопросы и отзывы 0
Доступ к категории Рекомендации 0
Доступ к категории Чат с покупателями 0
Токен только на чтение 0
'''
check_wb_token_rights(34)
'''
For number: 34
Доступ к категории Контент 1
Доступ к категории Аналитика 0
Доступ к категории Цены и скидки 0
Доступ к категории Маркетплейс 0
Доступ к категории Статистика 1
Доступ к категории Продвижение 0
Доступ к категории Вопросы и отзывы 0
Доступ к категории Рекомендации 0
Доступ к категории Чат с покупателями 0
Токен только на чтение 0
'''
check_wb_token_rights(62)
'''
For number: 62
Доступ к категории Контент 1
Доступ к категории Аналитика 1
Доступ к категории Цены и скидки 1
Доступ к категории Маркетплейс 1
Доступ к категории Статистика 1
Доступ к категории Продвижение 0
Доступ к категории Вопросы и отзывы 0
Доступ к категории Рекомендации 0
Доступ к категории Чат с покупателями 0
Токен только на чтение 0
'''
is_bit_set_in_position
и есть первый способ определить позицию бита в числе. Перечислю еще несколько способов:
C использованием побитового оператора И
def is_bit_set_in_position(n, pos):
mask = 1 << pos
return (n & mask) != 0
def check_wb_token_rights(bit_mask):
print(f'For number: {bit_mask}')
print('Доступ к категории Контент', is_bit_set_in_position(bit_mask, 1))
print('Доступ к категории Аналитика', is_bit_set_in_position(bit_mask, 2))
print('Доступ к категории Цены и скидки', is_bit_set_in_position(bit_mask, 3))
print('Доступ к категории Маркетплейс', is_bit_set_in_position(bit_mask, 4))
print('Доступ к категории Статистика', is_bit_set_in_position(bit_mask, 5))
print('Доступ к категории Продвижение', is_bit_set_in_position(bit_mask, 6))
print('Доступ к категории Вопросы и отзывы', is_bit_set_in_position(bit_mask, 7))
print('Доступ к категории Рекомендации', is_bit_set_in_position(bit_mask, 8))
print('Доступ к категории Чат с покупателями', is_bit_set_in_position(bit_mask, 9))
print('Токен только на чтение', is_bit_set_in_position(bit_mask, 30))
print('\n')
check_wb_token_rights(62)
'''
For number: 62
Доступ к категории Контент True
Доступ к категории Аналитика True
Доступ к категории Цены и скидки True
Доступ к категории Маркетплейс True
Доступ к категории Статистика True
Доступ к категории Продвижение False
Доступ к категории Вопросы и отзывы False
Доступ к категории Рекомендации False
Доступ к категории Чат с покупателями False
Токен только на чтение False
'''
C использованием оператора сдвига направо ">>"
def is_bit_set_in_position(n, pos):
return (n >> pos) & 1
def check_wb_token_rights(bit_mask):
print(f'For number: {bit_mask}')
print('Доступ к категории Контент', is_bit_set_in_position(bit_mask, 1))
print('Доступ к категории Аналитика', is_bit_set_in_position(bit_mask, 2))
print('Доступ к категории Цены и скидки', is_bit_set_in_position(bit_mask, 3))
print('Доступ к категории Маркетплейс', is_bit_set_in_position(bit_mask, 4))
print('Доступ к категории Статистика', is_bit_set_in_position(bit_mask, 5))
print('Доступ к категории Продвижение', is_bit_set_in_position(bit_mask, 6))
print('Доступ к категории Вопросы и отзывы', is_bit_set_in_position(bit_mask, 7))
print('Доступ к категории Рекомендации', is_bit_set_in_position(bit_mask, 8))
print('Доступ к категории Чат с покупателями', is_bit_set_in_position(bit_mask, 9))
print('Токен только на чтение', is_bit_set_in_position(bit_mask, 30))
print('\n')
check_wb_token_rights(62)
'''
For number: 62
Доступ к категории Контент 1
Доступ к категории Аналитика 1
Доступ к категории Цены и скидки 1
Доступ к категории Маркетплейс 1
Доступ к категории Статистика 1
Доступ к категории Продвижение 0
Доступ к категории Вопросы и отзывы 0
Доступ к категории Рекомендации 0
Доступ к категории Чат с покупателями 0
Токен только на чтение 0
'''
С использованием деления с округлением в меньшую степень остатка от деления целого числа
def is_bit_set_in_position(n, pos):
return (n % (1 << (pos + 1))) // (1 << pos)
def check_wb_token_rights(bit_mask):
print(f'For number: {bit_mask}')
print('Доступ к категории Контент', is_bit_set_in_position(bit_mask, 1))
print('Доступ к категории Аналитика', is_bit_set_in_position(bit_mask, 2))
print('Доступ к категории Цены и скидки', is_bit_set_in_position(bit_mask, 3))
print('Доступ к категории Маркетплейс', is_bit_set_in_position(bit_mask, 4))
print('Доступ к категории Статистика', is_bit_set_in_position(bit_mask, 5))
print('Доступ к категории Продвижение', is_bit_set_in_position(bit_mask, 6))
print('Доступ к категории Вопросы и отзывы', is_bit_set_in_position(bit_mask, 7))
print('Доступ к категории Рекомендации', is_bit_set_in_position(bit_mask, 8))
print('Доступ к категории Чат с покупателями', is_bit_set_in_position(bit_mask, 9))
print('Токен только на чтение', is_bit_set_in_position(bit_mask, 30))
print('\n')
check_wb_token_rights(62)
'''
For number: 62
Доступ к категории Контент 1
Доступ к категории Аналитика 1
Доступ к категории Цены и скидки 1
Доступ к категории Маркетплейс 1
Доступ к категории Статистика 1
Доступ к категории Продвижение 0
Доступ к категории Вопросы и отзывы 0
Доступ к категории Рекомендации 0
Доступ к категории Чат с покупателями 0
Токен только на чтение 0
'''