12/09/2014, 04:42 AM
(Última modificación: 05/01/2017, 02:18 AM por meTaLiCroSS.)
NOTA: Antes de dedicarte a leer lo que pondre en este tutorial, debes manejar bien toda la materia de los Bits, Bitsums y Operadores Bitwise.
Ya que no esta puesto como algo, y pocos saben al respecto, aqui les explicare como funciona la compresion de dígitos, o tambien conocido como packing.
Recalcaremos un par de cosas, las cuales nos serviran en el lapso del tema.
► En PAWN las variables creadas (pedorramente) utilizan 4 bytes, a excepcion de las catalogadas char.
► 1 byte equivale a 8 bits, lo que podria ser entendido a la ligera como 8 pequeñas casillas las cuales son o 0 o 1.
► 1 byte rellenado entero equivale a 255 (bin=11111111)
Ahora con estos 3 puntos recalcados, podrán entender la idea que postularé ahora. Les dare a mostrar el ejemplo más comun de packing.
Hagamos que tengo un array de 3 casillas, las cuales contienen un color RGB. Lo puedo demostrar de la siguiente forma.
Lo cual en binario se vería mostrado como si fuese:
TIP: Usando de prefijo 0b estamos declarando un numero en binario. Esto nos ayudaría a redactarlo directamente como:
Pero ahora, intentaremos usar la menor cantidad de memoria y vamos a querer reducir esos 3 numeros. (ya que se ve enfermo de horrible tener 3 simples bloques de datos en tu super base de datos para solo guardar un podrido color.)
Conociendo los operadores >> , << , | e & y además recordando que una variable creada en pawn es un signed de 4 bytes, pasaremos ahora a la aplicacion.
Probaremos lo siguiente: Sabemos, que un color no puede valer más de 255. No es por magia o por algun hechizo de antaño, es simplemente porque es 1 byte relleno, y el computador los entiende asi.
Es por esto que un color RGB no tendrá más de 8 bits. En memoria, iColor se visualiza asi:
iColor[0]
00000000000000000000000010000000
iColor[1]
00000000000000000000000001000000
iColor[2]
00000000000000000000000011111111
En una simple variable usamos 8 bits, cuando tenemos 24 disponibles. ¡Podriamos perfectamente poner los otros 2 bytes en 1 variable! ¿Como?
Pues claro, si sabemos que cada uno ocupara 8 bits, peguemos los otros 2 delante de este. Partamos por el B de RGB, osea, iColor[2]. Crearemos una var auxiliar para ver el procedimiento paso a paso.
iPacked
00000000000000000000000000000000
Añadamosle el B:
iPacked
00000000000000000000000011111111
Genial, una asignacion sencilla. Pero el drama, es, como metemos los otros 2?
Para eso existe el tan lerdo operador <<. Los primeros 8 espacios de iPacked estan siendo usados por el color B. Entonces, usaremos los 8 siguientes disponibles, moviendo 8 veces los bits a la izquierda. Usaremos el operador | para poder unir ambos contenidos.
iPacked
00000000000000000000000011111111
iColor[1]
00000000000000000000000001000000
iColor[1] << 8
00000000000000000100000000000000 (lo que está en negrita, son los 8 bits saltados)
iPacked | (iColor[1] << 8)
00000000000000000100000011111111
¿Captas la idea?
La variable iPacked ahora esta usando 16 bits. Necesitamos guardar a R en la variable. Siendo que hay 16 bits ocupados, nos saltaremos esos mismo 16 bits para ocupar 8 de los 16 que estan desocupados. (16 desocupados, 16 libres, si, leiste bien)
iPacked
00000000000000000100000011111111
iColor[0]
00000000000000000000000010000000
iColor[0] << 16
00000000100000000000000000000000
iPacked | (iColor[0] << 16)
00000000100000000100000011111111
Si lo pasamos por un convertidor, el resultado en decimal nos daria 8405247, pero eso no nos importa
Evitando explicaciones, lo podemos hacer funcion facilmente.
Ahora que supimos comprimirlo, usaremos la misma ingenieria para descomprimirlo. Para esto solo se requiere imaginacion.
Si anteriormente, movimos a la izquierda y unimos, ahora moveremos a la derecha y quitamos. ¿Como?
Tenemos a iPacked como:
iPacked
00000000100000000100000011111111
Partamos por lo facil. Tenemos los 8 bits de B al principio.
11111111
Debemos separar este tramo del trozo entero. ¿Pero como?
Aqui es donde entramos con el operador &. El operador & o 'AND' retornara los bits que en ambos lados esten asignados. Si uno de los 2 lados no es 1, devolvera 0.
Es por eso que para poder sustraer todos los bits de un tramo, debemos preguntar crear la condicion necesaria. Debemos obtener todo posible bit en esa area, entonces: 1 byte rellenado = 255 = 0b11111111.
Pero se ve mejor expresado como 0xFF, en hexadecimal.
iPacked
00000000100000000100000011111111
0xFF
00000000000000000000000011111111
iPacked & 0xFF
00000000000000000000000011111111
Excellent. Ahora, debemos sustraer los otros 8 bits que quedan en iPacked. Pero ojo, los 8 bits del color B siguen ahí. Debemos mover los 8 bits de G a la derecha para sustraerlos como lo hicimos con B. Ahi es donde usamos el operador >> (a la derecha).
OJO: Al mover bits a un lado, si por alguna razon mueves una cierta cantidad que no encajasen, estos simplemente desaparecen.
Pero como ya obtuvimos los 8 primeros bits de iPacked, ya dan lo mismo. Podemos eliminarlos facilmente moviendo todo a la derecha.
iPacked
00000000100000000100000011111111
iPacked >> 8
00000000000000001000000001000000
0xFF
00000000000000000000000011111111
(iPacked >> 8) & 0xFF
00000000000000000000000001000000
Listo, ya tenemos a G. Falta R, usemos la misma ingenieria: Ya que los valores persisten en iPacked, ignoraremos a G y B moviendo 16 bits a la derecha, para que me queden los 8 bits de R al principio.
iPacked
00000000100000000100000011111111
iPacked >> 16
00000000000000000000000010000000
0xFF
00000000000000000000000011111111
(iPacked >> 16) & 0xFF
00000000000000000000000010000000
Voilà, tenemos a R, pero... ¿Te percataste que hay algo inutil en el ultimo procedimiento? Si, al hacer desaparecer los primeros 16 bits, quedan solo 8 bits. Entonces, ¿para que usamos & 0xFF si no iba a tener más de 8 bits?
Depende de que tipo de valores estes sacando. Si sabes que no habran mas bits, pues no lo utilices. Pero si por alguna extraña razon sabes que habra algun bit asignado despues de los 24 bits que deduces que forman un color, pues utilizalo. (y es probable quizas, porque los sobrantes 8 bits pueden ser usado como alpha, formando un color RGBA, pero es un por decir, por eso depende del caso)
Ya con todo explicado, procedamos a hacerlo funcion.
Lo explicado aqui es un ejemplo de esta manera de pensar. Otro buen ejemplo de esto, son las Direcciones IP que, mediante este mismo metodo (ya que sus rangos de valores son [0, 255] al igual que un color) pueden ser comprimidos.
La manera de realizar esto con cualquier cosa que queramos comprimir, es la siguiente.
1) Calcula/deduce el rango de valores que tendran esos valores, valga la redundancia. Si incluye valores negativos, incluye un bit extra (el cual estara en cada particion, este definira su signo)
2) Al decimal máximo posible que pueda contener cada valor, obten su potencia de 2 inferior más cercana (valor binario).
3) A este valor, calculale sus bits posibles.
4) Obten cuantos valores vas a comprimir. Si la cantidad de bits por valor, multiplicado la cantidad de valores, excede a 32, puedes partir por ver otro metodo, o usar otra variable.
Por ejemplo:
Tengo un Sistema de puntos, y sé que no pueden exceder los 500 puntos, y en si no pueden tener puntos negativos.
1) Rango de valores: [0, 500]
2) Mi decimal maximo es 500, esta entre los binarios 256 y 512, por lo que su potencia de 2 inferior más cercana vendria siendo 256.
3) 256 = 100000000, son 9 bits, los cuales rellenados (bin=111111111) son equivalentes a 511, es decir, que 500 puede encajar en 9 bits. (todo calza)
4) Son 3 valores los que quiero guardar (ej.: puntos de Kills, puntos de Precision, y puntos de Asistencia), osea, 9 bits * 3 = 27 bits, no superamos 32, estamos bien.
ComprimirPuntos()
00000000000000000000000000000000
(rojo -> iPuntosAsistencia)
(verde -> iPuntosPrecision)
(azul -> iPuntosKills)
Descomprimiendo sería:
Arte.
Para facilitarles el trabajo, utilizen este sitio como referencia. Sirve mucho para este tipo de cosas.
Espero cualquier respuesta sobre el tema. Me llevo un buen tiempo redactarlo bien y que sea entendible.
Ya que no esta puesto como algo, y pocos saben al respecto, aqui les explicare como funciona la compresion de dígitos, o tambien conocido como packing.
Recalcaremos un par de cosas, las cuales nos serviran en el lapso del tema.
► En PAWN las variables creadas (pedorramente) utilizan 4 bytes, a excepcion de las catalogadas char.
► 1 byte equivale a 8 bits, lo que podria ser entendido a la ligera como 8 pequeñas casillas las cuales son o 0 o 1.
► 1 byte rellenado entero equivale a 255 (bin=11111111)
Ahora con estos 3 puntos recalcados, podrán entender la idea que postularé ahora. Les dare a mostrar el ejemplo más comun de packing.
Hagamos que tengo un array de 3 casillas, las cuales contienen un color RGB. Lo puedo demostrar de la siguiente forma.
Código PHP:
new iColor[3] = { 128, 64, 255 }
Lo cual en binario se vería mostrado como si fuese:
Código:
iColor[0] -> 10000000
iColor[1] -> 01000000
iColor[2] -> 11111111
TIP: Usando de prefijo 0b estamos declarando un numero en binario. Esto nos ayudaría a redactarlo directamente como:
Código PHP:
new iColor[3] = { 0b10000000, 0b01000000, 0b11111111 }
Pero ahora, intentaremos usar la menor cantidad de memoria y vamos a querer reducir esos 3 numeros. (ya que se ve enfermo de horrible tener 3 simples bloques de datos en tu super base de datos para solo guardar un podrido color.)
Conociendo los operadores >> , << , | e & y además recordando que una variable creada en pawn es un signed de 4 bytes, pasaremos ahora a la aplicacion.
Probaremos lo siguiente: Sabemos, que un color no puede valer más de 255. No es por magia o por algun hechizo de antaño, es simplemente porque es 1 byte relleno, y el computador los entiende asi.
Es por esto que un color RGB no tendrá más de 8 bits. En memoria, iColor se visualiza asi:
iColor[0]
00000000000000000000000010000000
iColor[1]
00000000000000000000000001000000
iColor[2]
00000000000000000000000011111111
En una simple variable usamos 8 bits, cuando tenemos 24 disponibles. ¡Podriamos perfectamente poner los otros 2 bytes en 1 variable! ¿Como?
Pues claro, si sabemos que cada uno ocupara 8 bits, peguemos los otros 2 delante de este. Partamos por el B de RGB, osea, iColor[2]. Crearemos una var auxiliar para ver el procedimiento paso a paso.
Código PHP:
new iPacked;
iPacked
00000000000000000000000000000000
Añadamosle el B:
Código PHP:
iPacked = iColor[2]
iPacked
00000000000000000000000011111111
Genial, una asignacion sencilla. Pero el drama, es, como metemos los otros 2?
Para eso existe el tan lerdo operador <<. Los primeros 8 espacios de iPacked estan siendo usados por el color B. Entonces, usaremos los 8 siguientes disponibles, moviendo 8 veces los bits a la izquierda. Usaremos el operador | para poder unir ambos contenidos.
Código PHP:
iPacked = iPacked | (iColor[1] << 8)
iPacked
00000000000000000000000011111111
iColor[1]
00000000000000000000000001000000
iColor[1] << 8
00000000000000000100000000000000 (lo que está en negrita, son los 8 bits saltados)
iPacked | (iColor[1] << 8)
00000000000000000100000011111111
¿Captas la idea?
La variable iPacked ahora esta usando 16 bits. Necesitamos guardar a R en la variable. Siendo que hay 16 bits ocupados, nos saltaremos esos mismo 16 bits para ocupar 8 de los 16 que estan desocupados. (16 desocupados, 16 libres, si, leiste bien)
Código PHP:
iPacked = iPacked | (iColor[0] << 16)
iPacked
00000000000000000100000011111111
iColor[0]
00000000000000000000000010000000
iColor[0] << 16
00000000100000000000000000000000
iPacked | (iColor[0] << 16)
00000000100000000100000011111111
Si lo pasamos por un convertidor, el resultado en decimal nos daria 8405247, pero eso no nos importa
Evitando explicaciones, lo podemos hacer funcion facilmente.
Código PHP:
stock PackRGB(const iColors[3])
{
// B G R
return iColors[2] | (iColors[1]<<8) | (iColors[0]<<16)
}
// O también macro, para los fanaticos
#define PackRGB(%1) (%1[2] | (%1[1]<<8) | (%1[0]<<16))
Ahora que supimos comprimirlo, usaremos la misma ingenieria para descomprimirlo. Para esto solo se requiere imaginacion.
Si anteriormente, movimos a la izquierda y unimos, ahora moveremos a la derecha y quitamos. ¿Como?
Tenemos a iPacked como:
iPacked
00000000100000000100000011111111
Partamos por lo facil. Tenemos los 8 bits de B al principio.
11111111
Debemos separar este tramo del trozo entero. ¿Pero como?
Aqui es donde entramos con el operador &. El operador & o 'AND' retornara los bits que en ambos lados esten asignados. Si uno de los 2 lados no es 1, devolvera 0.
Es por eso que para poder sustraer todos los bits de un tramo, debemos preguntar crear la condicion necesaria. Debemos obtener todo posible bit en esa area, entonces: 1 byte rellenado = 255 = 0b11111111.
Pero se ve mejor expresado como 0xFF, en hexadecimal.
iPacked
00000000100000000100000011111111
0xFF
00000000000000000000000011111111
iPacked & 0xFF
00000000000000000000000011111111
Excellent. Ahora, debemos sustraer los otros 8 bits que quedan en iPacked. Pero ojo, los 8 bits del color B siguen ahí. Debemos mover los 8 bits de G a la derecha para sustraerlos como lo hicimos con B. Ahi es donde usamos el operador >> (a la derecha).
OJO: Al mover bits a un lado, si por alguna razon mueves una cierta cantidad que no encajasen, estos simplemente desaparecen.
Pero como ya obtuvimos los 8 primeros bits de iPacked, ya dan lo mismo. Podemos eliminarlos facilmente moviendo todo a la derecha.
iPacked
00000000100000000100000011111111
iPacked >> 8
00000000000000001000000001000000
0xFF
00000000000000000000000011111111
(iPacked >> 8) & 0xFF
00000000000000000000000001000000
Listo, ya tenemos a G. Falta R, usemos la misma ingenieria: Ya que los valores persisten en iPacked, ignoraremos a G y B moviendo 16 bits a la derecha, para que me queden los 8 bits de R al principio.
iPacked
00000000100000000100000011111111
iPacked >> 16
00000000000000000000000010000000
0xFF
00000000000000000000000011111111
(iPacked >> 16) & 0xFF
00000000000000000000000010000000
Voilà, tenemos a R, pero... ¿Te percataste que hay algo inutil en el ultimo procedimiento? Si, al hacer desaparecer los primeros 16 bits, quedan solo 8 bits. Entonces, ¿para que usamos & 0xFF si no iba a tener más de 8 bits?
Depende de que tipo de valores estes sacando. Si sabes que no habran mas bits, pues no lo utilices. Pero si por alguna extraña razon sabes que habra algun bit asignado despues de los 24 bits que deduces que forman un color, pues utilizalo. (y es probable quizas, porque los sobrantes 8 bits pueden ser usado como alpha, formando un color RGBA, pero es un por decir, por eso depende del caso)
Ya con todo explicado, procedamos a hacerlo funcion.
Código PHP:
stock UnpackRGB(iPacked, iOutcolor[3])
{
iOutcolor[2] = iPacked & 0xFF
iOutcolor[1] = (iPacked >> 8) & 0xFF
iOutcolor[0] = (iPacked >> 16) & 0xFF
}
Lo explicado aqui es un ejemplo de esta manera de pensar. Otro buen ejemplo de esto, son las Direcciones IP que, mediante este mismo metodo (ya que sus rangos de valores son [0, 255] al igual que un color) pueden ser comprimidos.
La manera de realizar esto con cualquier cosa que queramos comprimir, es la siguiente.
1) Calcula/deduce el rango de valores que tendran esos valores, valga la redundancia. Si incluye valores negativos, incluye un bit extra (el cual estara en cada particion, este definira su signo)
2) Al decimal máximo posible que pueda contener cada valor, obten su potencia de 2 inferior más cercana (valor binario).
3) A este valor, calculale sus bits posibles.
4) Obten cuantos valores vas a comprimir. Si la cantidad de bits por valor, multiplicado la cantidad de valores, excede a 32, puedes partir por ver otro metodo, o usar otra variable.
Por ejemplo:
Tengo un Sistema de puntos, y sé que no pueden exceder los 500 puntos, y en si no pueden tener puntos negativos.
1) Rango de valores: [0, 500]
2) Mi decimal maximo es 500, esta entre los binarios 256 y 512, por lo que su potencia de 2 inferior más cercana vendria siendo 256.
3) 256 = 100000000, son 9 bits, los cuales rellenados (bin=111111111) son equivalentes a 511, es decir, que 500 puede encajar en 9 bits. (todo calza)
4) Son 3 valores los que quiero guardar (ej.: puntos de Kills, puntos de Precision, y puntos de Asistencia), osea, 9 bits * 3 = 27 bits, no superamos 32, estamos bien.
Código PHP:
ComprimirPuntos(iPuntosKills, iPuntosPrecision, iPuntosAsistencia)
{
return iPuntosKills | (iPuntosPrecision<<9) | (iPuntosAsistencia<<18)
// 9*0, 9*1, y 9*2 respectivamente
}
ComprimirPuntos()
00000000000000000000000000000000
(rojo -> iPuntosAsistencia)
(verde -> iPuntosPrecision)
(azul -> iPuntosKills)
Descomprimiendo sería:
Código PHP:
DescomprimirPuntos(iCompresion, &iPuntosKills, &iPuntosPrecision, &iPuntosAsistencia)
{
// 9 bits rellenos = 511 = 0x1FF en hexadecimal
iPuntosKills = iCompresion& 0x1FF
iPuntosPrecision = (iCompresion>> 9) & 0x1FF
iPuntosAsistencia = (iCompresion>> 18) & 0x1FF
}
Arte.
Para facilitarles el trabajo, utilizen este sitio como referencia. Sirve mucho para este tipo de cosas.
Espero cualquier respuesta sobre el tema. Me llevo un buen tiempo redactarlo bien y que sea entendible.
Strings en goldsrc (STRING, ALLOC_STRING, MAKE_STRING) - GoldSrc: gamedll, hlsdk, etc - Obteniendo nombres de texturas de mapas - FindEntityByString y derivados - Detectar Ataques de Knife - El Parametro fNoMonsters de los Traces - Funcion: AddToFullPack - Compresión de digitos - Native: register_event
► Si vas a pedirme ayuda con code vía Mensaje Privado, anda pensando primero como me vas a cargar la PayPal... ◄
► Si vas a pedirme ayuda con code vía Mensaje Privado, anda pensando primero como me vas a cargar la PayPal... ◄