namespace Base32 { public errordomain Base32Error { INCORRECT_PADDING, INCORRECT_MAP01; } private const string base32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; private Array _base32_tab = null; private GLib.HashTable _b32rev = null; public GLib.Bytes b32encode(GLib.Bytes input) { if (_base32_tab == null) { _base32_tab = new Array(); var b32tab = new GLib.Bytes(base32_alphabet.data); foreach (var a in b32tab.get_data()) { foreach (var b in b32tab.get_data()) { _base32_tab.append_val("%c%c".printf(a, b)); } } } var leftover = input.length % 5; var s = new GLib.ByteArray(); s.append(input.get_data()); if (leftover > 0) { var padding = new uint8[leftover + 1]; for (var i = 0; i <= leftover; i++) { padding[i] = 0; } s.append(padding); } string encoded = ""; var length = 0; for (var i = 0; i < s.len; i += 5) { var next5 = s.data[i:i + 5]; uint64 c = ((uint64)next5[0] << 32) + ((uint64)next5[1] << 24) + ((uint64)next5[2] << 16) + ((uint64)next5[3] << 8) + (uint64)next5[4]; encoded += _base32_tab.index((uint)(c >> 30)); encoded += _base32_tab.index((uint)((c >> 20) & 0x3ff)); encoded += _base32_tab.index((uint)((c >> 10) & 0x3ff)); encoded += _base32_tab.index((uint)(c & 0x3ff)); length += 8; } int padder = 0; if (leftover == 1) { padder = -6; } else if (leftover == 2) { padder = -4; } else if (leftover == 3) { padder = -3; } else if (leftover == 4) { padder = -1; } for (var i = padder; i < 0; i++) { encoded.data[encoded.length + i] = '='; } return new GLib.Bytes(encoded.data); } public GLib.Bytes b32decode(GLib.Bytes input, bool casefold, char map01) throws Base32Error { if (_b32rev == null) { _b32rev = new GLib.HashTable(null, null); for (var i = 0; i < base32_alphabet.length; i++) { _b32rev[base32_alphabet[i]] = i; } } if (input.length % 8 != 0) { throw new Base32Error.INCORRECT_PADDING("Incorrect padding"); } var s = new GLib.ByteArray(); if (map01 != 0) { if ((map01 != 'L') && (map01 != 'I')) { throw new Base32Error.INCORRECT_MAP01("Incorrect map01 value"); } var translated = new uint8[input.length]; var idata = input.get_data(); for (var i = 0; i < input.length; i++) { if (idata[i] == 'O') { translated[i] = '0'; } else if (idata[i] == map01) { translated[i] = '1'; } else { translated[i] = idata[i]; } } s.append(translated); } else { s.append(input.get_data()); } if (casefold) { for (var i = 0; i < s.len; i++) { s.data[i] = ((char)s.data[i]).toupper(); } } var padchars = 0; while (s.data[s.len - padchars - 1] == '=') { padchars++; } s.remove_range(s.len - padchars, padchars); var decoded = new GLib.ByteArray(); uint64 acc = 0; for (var i = 0; i < input.length; i += 8) { var quanta = s.data[i: i + 8]; acc = 0; for (var j = 0; j < 8; j++) { var c = quanta[j]; acc = (acc << 5) + _b32rev[c]; } uint8 res[5]; for (var j = 0; j < 5; j++) { res[j] = (uint8)((acc >> (32 - j * 8)) & 0xff); } decoded.append(res); } if (padchars > 0) { uint8 last[5]; for (var j = 0; j < 5; j++) { last[j] = (uint8)((acc >> (32 - j * 8)) & 0xff); } var pad_offset = 0; if (padchars == 1) { pad_offset = -1; } else if (padchars == 3) { pad_offset = -2; } else if (padchars == 4) { pad_offset = -3; } else if (padchars == 6) { pad_offset = -4; } else { throw new Base32Error.INCORRECT_PADDING("Incorrect padding"); } decoded.remove_range(decoded.len - 5, 5); decoded.append(last[0:5 + pad_offset]); } return new GLib.Bytes(decoded.data); } }