Máscaras de bits

Una máscara de bits son datos para operaciones a nivel de bits. Por ejemplo para el conjunto de los 10 primeros números naturales:

U = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

La máscara que marca los impares es:

M = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]

Mediante la operación NOT sobre la máscara obtenemos los pares:

M = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]

Dadas dos máscaras, A y B, la operación OR (∨) nos proporciona la unión de ambas A ∪ B mientras que AND (∧) la intersección A ∩ B :

A = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]

B = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]

A ∧ B = A ∩ B = [0, 0, 0, 0, 1, 0, 1, 0]

A ∨ B = A ∪ B = [1, 0, 1, 0, 1, 1, 1, 1, 1, 1]

Las máscaras de bits tienen diferentes usos.

Sirven para definir rangos de IPs y ACL

ACL es el acrónimo de listas de control de acceso. En el protocolo TCP/IP las direcciones IP vienen acompañas de su máscara. Si ejecuto ifconfig en mi ordenador aparece la siguiente información:

ifconfig

IP y máscara

Es gracias a la máscara que los demás ordenadores de esta red pueden saber si el mío pertenece a la misma red local o a una red remota. Veamos como lo hace:

11000000.10101000.00000001.01100111 => 192.168.1.103 IP

11111111.11111111.11111111.00000000 => 255.255.255.0 Máscara de red

Mediante ceros las máscaras de red indican qué octeto es el significativo para identificar al ordenador, en este caso es el último octeto, cuyo valor es 103. Mientras que con unos indica el identificador de la red, que en este ejemplo es el de las redes locales o redes de clase C: 192.168.1

Sirven para gráficos 2D

En los juegos de no hace tantos años, antes de la llegada de las 3D, mediante una máscara de bits se indicaba que parte del sprite debe transparentar con el fondo por el que se mueve.

Sprites y máscaras de bits

Sprites y máscaras de bits

Aún hoy en día se siguen usando, por ejemplo cuando hace pocos meses Google añadió este juego a Google Maps:

Ms Pac-Man paseando por Madrid se encuentra la sede del PP…

cazando corruptos

¡Y no puede evitar ponerse cazar fantasmas corruptos pululando por la calle Génova!

Sirven para hacer código más legible

Se usan en funciones y métodos que esperan varios parámetros booleanos. La principal ventaja es simplificar el uso de la función, secundariamente se consigue una menor carga de la pila. Si tenemos en PHP la función:

function func ($param1, $param2, $param3, $param4)

Cuando la invoquemos lo más probable es que no nos acordemos de los parámetros y su orden. Es más práctico algo como esto:

const BIT_1 = 0b0001; // 1
const BIT_2 = 0b0010; // 2
const BIT_3 = 0b0100; // 4
const BIT_4 = 0b1000; // 8

function func ($bits) {
  if ($bits & BIT_1) {
    // Haz esto.
  }
  if ($bits & BIT_2) {
    // O esto otro.
  }
  if ($bits & BIT_3) {
    // También esto.
  }
  if ($bits & BIT_4) {
    // Y esto si también te lo piden.
  }
}

func(BIT_1 | BIT_3);

En PHP los operadores de bit AND y OR son & y | respectivamente. Muchas de las funciones incluidas en PHP usan está técnica, al igual que en otros lenguajes. He aquí unos ejemplos:

filter_var($string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_LOW)

html_entity_decode($string, ENT_QUOTES | ENT_XML1, 'UTF-8')

El error_reporting para especificar el nivel de errores en php.ini y la función error_reporting() para modificarlo en tiempo de ejecución.

Sirven para el control de permisos

Usar máscaras de bits y operaciones a nivel de bit para controlar los permisos de usuarios o de los grupos a los que pertenecen puede llegar a ser inviable en aplicaciones realmente grandes, pero donde sea cómodo aplicarlas se conseguirá una velocidad y reducción del tamaño de la base de datos sin igual. En el caso de la base de datos permiten pasar de tener una o más tablas donde almacenar los permisos a un solo campo en la tabla de usuarios, permitiendo olvidarnos entonces de costosas consultas con Joins.

Aunque su uso para estos menesteres no sea recomendable, veamos a modo de curiosidad cómo funcionan. La siguiente tabla presenta unos permisos mapeados para un gestor de contenidos:

cambiar permisos crear perfil editar perfil borrar perfil crear borrador
512 256 128 64 32
editar entrada borrar entrada publicar entrada editar entrada borrar entrada
16 8 4 2 1

Si en la campo “permisos” de un usuario tenemos el número 14, sabemos que este en binario es 1110. También sabemos que el número máximo es 512, 1000000000 en binario, por lo tanto concatenemos por la izquierda los ceros que faltan: 0000001110. Sigue siendo 14 en binario y si ponemos cada dígito en la tabla donde mapeamos los permisos, tendremos que con 1, es decir, activos, están los que permiten editar, borrar y publicar una entrada. A los demás permisos les corresponde un cero y por lo tanto el usuario no los tiene. Un usuario con todas las concesiones sería 1111111111, por lo tanto en el campo correspondiente de la tabla “usuarios” tendría almacenado el número 1023.

Antes hemos visto como implementa PHP los operadores bit a bit; realmente todos los lenguajes los implementan, observemos ahora la siguiente consulta SQL en MySQL:

SELECT * FROM usuarios WHERE 512 & permisos

Esta consulta nos daría todos los usuarios que tienen el 512, “cambiar permisos”, activado. El operador a nivel de bits & devolverá verdadero sólo en los casos en que sean 1 los bits a su izquierda y lo sean también en la misma posición a la derecha. Recordemos que 512 en binario es 1000000000 por lo que la condición del WHERE será sólo verdadera en los registros cuya columna “permisos” el bit más significativo (el de la izquierda) valga 1. Por supuesto también se pueden usar otros operadores lógicos, no sólo AND.

Como hemos visto, aunque las operaciones a nivel de bit parezcan más de tiempos en los que los recursos de los ordenadores, RAM y CPU, eran muy limitados, siguen teniendo su utilidad.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.