1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
| CREATE OR REPLACE PACKAGE PKG_B64 AS
FUNCTION F_ENCODE (p_chaine IN VARCHAR2) RETURN VARCHAR2;
FUNCTION F_DECODE(p_chaine IN VARCHAR2) RETURN VARCHAR2;
FUNCTION F_ENCODE_RAW (p_chaine IN RAW) RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY PKG_B64
IS
v_b64 VARCHAR2(24); -- binaire de 3 car Ascii => 4 car b64
-- fonction decimal en binaire
FUNCTION dec2bin (p_dec IN number) RETURN varchar2 IS
binval varchar2(64);
v number := p_dec;
BEGIN
WHILE v > 0
LOOP
binval := MOD(v, 2) || binval;
v := TRUNC( v / 2 );
END LOOP;
RETURN binval;
END dec2bin;
-----+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
FUNCTION bin2dec (binval IN varchar2) RETURN number IS
result number := 0;
BEGIN
FOR i IN 1..LENGTH(binval)
LOOP
result := (result * 2) + TO_NUMBER(SUBSTR(binval, i, 1));
END LOOP;
RETURN result;
END bin2dec;
-----+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
FUNCTION car64_encode(p_num IN number) RETURN varchar2
IS
BEGIN
-- Renvoit A-Z (0-25)
IF p_num BETWEEN 0 AND 25 THEN RETURN CHR(65 + p_num); END IF;
-- a-z (26-51)
IF p_num <= 51 THEN RETURN CHR(71 + p_num); END IF;
-- 0-9 (52-61)
IF p_num <= 61 THEN RETURN CHR(-4 + p_num); END IF;
IF p_num = 62 THEN RETURN '+'; END IF;
IF p_num = 63 THEN RETURN '/'; END IF;
RAISE_APPLICATION_ERROR(-20001, 'Pb num appel car64 :'|| p_num);
END;
FUNCTION car64_decode(p_car IN VARCHAR2) RETURN NUMBER
IS
BEGIN
-- A-Z :0-25
IF p_car BETWEEN 'A' AND 'Z' THEN RETURN ASCII(p_car) - 65; END IF;
-- a-z : 26-51
IF p_car BETWEEN 'a' AND 'z' THEN RETURN ASCII(p_car) - 71; END IF;
-- 0-9 : 52-61
IF p_car BETWEEN '0' AND '9' THEN RETURN ASCII(p_car) + 4; END IF;
IF p_car = '+' THEN RETURN 62; END IF;
IF p_car = '/' THEN RETURN 63; END IF;
RAISE_APPLICATION_ERROR(-20001, 'Pb num appel car64_decode :'|| p_car);
END;
-----+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
FUNCTION F_ENCODE (p_chaine IN VARCHAR2) RETURN VARCHAR2
IS
len NUMBER;
j NUMBER;
v_bin VARCHAR2(6);
v_retour VARCHAR2(2668); -- taille max : 4 * ceil(2000/3)
BEGIN
len := LENGTH(p_chaine);
-- On boucle par 3 car qui seront transformés en 4
FOR i IN 1 .. CEIL(len/3)
LOOP
v_b64 := LPAD(dec2bin(ASCII(SUBSTR(p_chaine, 3*(i-1)+1, 1))),8,'0');
-- si le car suivant existe
IF 3*(i-1)+2 <= len
THEN
v_b64 := v_b64 || LPAD(dec2bin(ASCII(SUBSTR(p_chaine, 3*(i-1)+2, 1))),8,'0');
-- si le car suivant existe
IF 3*(i-1)+3 <= len
THEN
v_b64 := v_b64 || LPAD(dec2bin(ASCII(SUBSTR(p_chaine, 3*(i-1)+3, 1))),8,'0');
ELSE
-- On rajoute des '0' pour arriver à 18 bits (3 car 64)
v_b64 := RPAD(v_b64, 18, '0');
END IF;
ELSE
-- On rajoute des '0' pour arriver à 12 bits (2 car 64)
v_b64 := RPAD(v_b64, 12, '0');
END IF;
-- DBMS_OUTPUT.put_line(v_b64);
-- On crée les caractères en B64 (codés sur 6 bits)
-- On lit par 6 bits x 4
FOR i IN 0..3
LOOP
v_bin := SUBSTR(v_b64, 6*i+1, 6);
-- DBMS_OUTPUT.put_line(i||':'||v_bin ||':'|| bin2dec(v_bin));
IF v_bin IS NOT NULL
THEN
v_retour := v_retour || car64_encode(bin2dec(v_bin));
ELSE
-- On complete par des = pour avoir des groupes de 4 car
v_retour := v_retour || LPAD('=', 4-i, '=');
EXIT;
END IF;
END LOOP;
END LOOP;
RETURN v_retour;
END F_ENCODE;
-----+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
FUNCTION F_DECODE(p_chaine IN VARCHAR2) RETURN VARCHAR2 IS
v_car VARCHAR2(1); len NUMBER;
v_retour VARCHAR2(2000);
BEGIN
len := NVL(LENGTH(p_chaine),-1);
IF MOD(len, 4) <> 0
THEN
RAISE_APPLICATION_ERROR(-20001, 'Taille non multiple de 4 ('|| LENGTH(p_chaine) ||')');
END IF;
IF LTRIM(LOWER(p_chaine)||'=', 'abcdefghijklmnopqrstuvwxyz0123456789+/') NOT IN ('=', '==', '===')
THEN
RAISE_APPLICATION_ERROR(-20001, 'Chaine contient des caractères autre que base64');
END IF;
-- On va lire par bloc de 4 caractères B64 pour les coder en 3 caractères Ascii
FOR i IN 1 .. (len/4)
LOOP
v_b64 := LPAD(dec2bin(car64_decode(SUBSTR(p_chaine, 4*(i-1)+1, 1))),6,'0')
||LPAD(dec2bin(car64_decode(SUBSTR(p_chaine, 4*(i-1)+2, 1))),6,'0');
IF SUBSTR(p_chaine, 4*(i-1)+3, 1) <> '='
THEN
v_b64 := v_b64 || LPAD(dec2bin(car64_decode(SUBSTR(p_chaine, 4*(i-1)+3, 1))),6,'0');
END IF;
IF SUBSTR(p_chaine, 4*(i-1)+4, 1) <> '='
THEN
v_b64 := v_b64 || LPAD(dec2bin(car64_decode(SUBSTR(p_chaine, 4*(i-1)+4, 1))),6,'0');
END IF;
--
-- DBMS_OUTPUT.put_line(v_b64);
-- On lit les binaires Ascii (8 bits)
v_retour := v_retour || CHR(bin2dec(SUBSTR(v_b64, 1, 8)));
IF LENGTH(v_b64) > 12 -- au moins 2 car (le reste sont des = enlevés)
THEN
v_retour := v_retour || CHR(bin2dec(SUBSTR(v_b64, 9, 8)));
END IF;
IF LENGTH(v_b64) = 24 -- tous les car
THEN
v_retour := v_retour || CHR(bin2dec(SUBSTR(v_b64, 17, 8)));
END IF;
END LOOP;
RETURN v_retour;
END;
FUNCTION F_HEX2BIN (p_car IN VARCHAR2) RETURN VARCHAR2
IS
BEGIN
IF p_car = '0' THEN RETURN '0000';
ELSIF p_car = '1' THEN RETURN '0001';
ELSIF p_car = '2' THEN RETURN '0010';
ELSIF p_car = '3' THEN RETURN '0011';
ELSIF p_car = '4' THEN RETURN '0100';
ELSIF p_car = '5' THEN RETURN '0101';
ELSIF p_car = '6' THEN RETURN '0110';
ELSIF p_car = '7' THEN RETURN '0111';
ELSIF p_car = '8' THEN RETURN '1000';
ELSIF p_car = '9' THEN RETURN '1001';
ELSIF p_car = 'A' THEN RETURN '1010';
ELSIF p_car = 'B' THEN RETURN '1011';
ELSIF p_car = 'C' THEN RETURN '1100';
ELSIF p_car = 'D' THEN RETURN '1101';
ELSIF p_car = 'E' THEN RETURN '1110';
ELSIF p_car = 'F' THEN RETURN '1111';
ELSE
RAISE_APPLICATION_ERROR(-20001, 'carac hexa invalide '|| p_car);
END IF;
END;
FUNCTION F_ENCODE_RAW (p_chaine IN RAW) RETURN VARCHAR2
IS
len NUMBER; len_ret NUMBER := 0;
j NUMBER;
v_bin VARCHAR2(6);
v_retour VARCHAR2(3000); -- taille max : 4 * ceil(2000/3)
BEGIN
-- Le raw doit avoir une taille multiple de 6 si on veut découper (2 car pour chaque code ascii, 3 car ascii pour 4 car B64).
-- Et obligatoirement multiple de 2
len := LENGTH(p_chaine);
IF MOD(len,2) <> 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'Taille du RAW non paire');
END IF;
-- On boucle par 6 car hexa qui seront transformés en 4 car B64
FOR i IN 1 .. CEIL(len/6)
LOOP
v_b64 := F_HEX2BIN(SUBSTR(p_chaine, 6*(i-1)+1, 1)) || F_HEX2BIN(SUBSTR(p_chaine, 6*(i-1)+2, 1));
-- si le car suivant existe
IF 6*(i-1)+3 <= len
THEN
v_b64 := v_b64 || F_HEX2BIN(SUBSTR(p_chaine, 6*(i-1)+3, 1)) || F_HEX2BIN(SUBSTR(p_chaine, 6*(i-1)+4, 1));
-- si le car suivant existe
IF 6*(i-1)+5 <= len
THEN
v_b64 := v_b64 || F_HEX2BIN(SUBSTR(p_chaine, 6*(i-1)+5, 1)) || F_HEX2BIN(SUBSTR(p_chaine, 6*(i-1)+6, 1));
ELSE
-- On rajoute des '0' pour arriver à 18 bits (3 car 64)
v_b64 := RPAD(v_b64, 18, '0');
END IF;
ELSE
-- On rajoute des '0' pour arriver à 12 bits (2 car 64)
v_b64 := RPAD(v_b64, 12, '0');
END IF;
-- DBMS_OUTPUT.put_line(v_b64);
-- On crée les caractères en B64 (codés sur 6 bits)
-- On lit par 6 bits x 4
FOR i IN 0..3
LOOP
v_bin := SUBSTR(v_b64, 6*i+1, 6);
-- La chaine de retour est découpée en 64 caractères avec RC+LF
IF len_ret = 64
THEN
v_retour := v_retour || CHR(13)|| CHR(10);
len_ret := 1;
ELSE
len_ret := len_ret + 1;
END IF;
-- DBMS_OUTPUT.put_line(i||':'||v_bin ||':'|| bin2dec(v_bin));
IF v_bin IS NOT NULL
THEN
v_retour := v_retour || car64_encode(bin2dec(v_bin));
ELSE
-- On complete par des = pour avoir des groupes de 4 car
-- Je ne fais pas de retour à la ligne pour les = si le second dépasse les 64 car..
v_retour := v_retour || LPAD('=', 4-i, '=');
EXIT;
END IF;
END LOOP;
END LOOP;
RETURN v_retour;
END F_ENCODE_RAW;
END; |