Konversi GUID menjadi Base32-encoded String dengan C#

GUID merupakan kode pengenal universal yang kini sering digunakan dalam berbagai proyek piranti lunak (software projects). Keunggulan dari GUID dibanding nomor urut seperti int atau susunan karakter acak (random string) adalah kemudahan pembuatan nomor identitas tanpa perlu memperhatikan urutan, maupun pencegahan duplikasi nomor.

Meski demikian, salah satu kekurangan dari GUID adalah panjangnya karakter, terutama jika digunakan sebagai segmen URL. GUID direpresentasikan dalam 32 digit heksadesimal, yang dipisah oleh 4 buah strip (-), sehingga totalnya menjadi 36 karakter.

Untuk merepresentasikan GUID dalam bentuk string yang lebih pendek, hal yang bisa dilakukan adalah mengkonversi GUID ke dalam bentuk base32 encoded string. Meskipun konversi ini menghasilkan 26 karakter alfanumerik, lebih panjang ketimbang base64 yang menghasilkan 22 karakter saja, konversi GUID menjadi base32 tidak mencakup karakter non-alfanumerik seperti + dan / juga tidak membedakan huruf kapital dan huruf kecil (case-insensitive). Sehingga, base32 encoded GUID bisa diaplikasikan pada berbagai situasi.

Tutorial berikut ini menjelaskan langkah-langkah konversi dari objek Guid menjadi base32 encoded string dalam bahasa C# sebagaimana dipaparkan pada potongan kode fungsi GuidToBase32() berikut ini.

public static string GuidToBase32(Guid id)
{
    string chars = "abcdefghijklmnopqrstuvwxyz234567";
    string base32 = "";
    byte[] bytes = id.ToByteArray();

    // Base32 encodes 5 bytes to 8 characters
    for (var i = 0; i < bytes.Length; i += 5) 
    {
        // Get 5 bytes and convert it to uint64
        byte[] numBytes = new byte[8];
        Array.Copy(bytes, i, numBytes, 0, i + 5 <= bytes.Length ? 5 : bytes.Length - i);
        long num = BitConverter.ToUInt64(numBytes, 0);

        // Slice num into 5 bit each and map it to a character
        for (var j = 0; j < 8 && (j * 5 / 8) < bytes.Length - i; j++)
        {
            base32 += chars[(int)(num & 0x1F)];
            num >>= 5;
        }
    }
    return base32;
}

Penjelasan

Persiapan

Pertama-tama, kita siapkan pemetaan nilai angka menjadi 32 set huruf menurut standar RFC 4648 Base32 Alphabet. Dimulai dari huruf A untuk nilai 0 dan seterusnya hingga Z, kemudian dilanjutkan dengan karakter 2 sampai 7. Pemetaan ini bisa kita buat dengan variabel string seperti ini:

 string chars = "abcdefghijklmnopqrstuvwxyz234567";

Karakter base32 bisa dengan mudah diketahui dengan mengakses indeksnya, misalkan nilai 16 bisa diketahui dengan chars[16].

Berikutnya kita siapkan variabel string kosong base32 untuk menampung hasil konversi:

string base32 = "";

Untuk melakukan konversi, kita harus memprosesnya dalam bentuk bilangan biner, jadi ubah dulu objek Guid menjadi byte[] dengan metode ToByteArray():

byte[] bytes = id.ToByteArray(); 

Konversi biner dari basis 2 menjadi basis 32

Pada dasarnya, kita melakukan konversi bilangan biner dari basis 2 menjadi basis 32. Nilai tertinggi dari base32 adalah “7” sama dengan 31 dalam basis 10 atau 1 1111 dalam basis 2.

732 = 3110 = 1 11112

Mari kita gunakan GUID = 15f70893-d7dd-ec11-8ea1-000d3af41650 sebagai contoh kasus. Berikut ini ilustrasi konversi GUID dalam bentuk heksadesimal (sesuai urutan byte), biner dan base32-nya.

Disini dapat kita lihat bahwa satu karakter base32 bisa mewakili 5 bit. Karena data GUID yang kita miliki disimpan dalam satuan byte, di mana satu byte terdiri dari 8 bit, kita perlu cari kelipatan bergandanya, yaitu 40 bit = 5 byte.

Dengan kata lain, kita akan melakukan konversi per 40 bit atau 8 karakter base32 dan menyimpannya ke dalam 5 byte data. Untuk itu, kita lakukan perulangan setiap kelipatan 5 sepanjang jumlah byte dalam GUID.

// Base32 encodes 5 bytes to 8 characters
for (var i = 0; i < bytes.Length; i += 5) 
{
    // Get 5 bytes and convert it to uint64
    byte[] numBytes = new byte[8];
    Array.Copy(bytes, i, numBytes, 0, i + 5 <= bytes.Length ? 5 : bytes.Length - i);
    ulong num = BitConverter.ToUInt64(numBytes, 0);

    // ...
}

Dalam perulangan for, kita ambil 5 byte dari indeks i, atau jika sisanya kurang dari 5 lagi (total panjang GUID adalah 16 byte), ambil sebanyak sisanya.

Untuk memudahkan proses konversi, potongan data ini kita simpan dalam bentuk UInt64 (ulong). Fungsi Array.Copy() digunakan untuk mengambil 5 byte dan menyimpannya sementara dalam numBytes, lalu numBytes diubah menjadi ulong pada variabel num.

Sampai pada tahap ini kita memperoleh data biner untuk 5-byte pertama dalam variabel num.

Berikutnya, kita lakukan perulangan untuk mengambil 5 digit biner sebanyak 8 kali atau sebanyak yang tersedia jumlah byte kurang dari 5.

// Base32 encodes 5 bytes to 8 characters
for (var i = 0; i < bytes.Length; i += 5) 
{
    // ...

    // Slice num into 5 bit each and map it to a character
    for (var j = 0; j < 8 && (j * 5 / 8) < bytes.Length - i; j++)
    {
        base32 += chars[(int)(num & 0x1F)];
        num >>= 5;
    }
}

Jika menggunakan contoh GUID di atas, 5 byte pertama adalah 93 08 F7 15 DD (0xDD15F70893 dalam ulong). Untuk mengambil 5 bit pertama, kita lakukan operasi AND dengan 111112 atau 0x1F.

.. 08 93 = .. 0000 1000 1001 0011
      1F =              0001 1111
              ------------------- AND
                        0001 0011 = 19

Diperoleh nilai 100112 atau 1910. Indeks ke-19 adalah karakter T dalam base32. Karakter T ini kita tampung ke dalam variabel base32.

Selanjutnya, geser 5 bit ke kanan menggunakan operator bitwise shift >>

.. 0000 1000 1001 0011 >> 5 = .. 000 0100 0100
                         1F =        0001 1111
                                 ------------- AND
                                     0000 0100 = 4

Setelah 5 bit digeser, kita bisa ulangi operasi AND untuk 5 bit berikutnya, sehingga diperoleh 4 yaitu karakter E. Dan seterusnya 8 kali perulangan.

Di perulangan kedua ada tambahan kondisi (j * 5 / 8) < bytes.Length - i. Kondisi ini diperlukan karena kelompok terakhir hanya terdiri dari 1 byte saja, menghasilkan 2 karakter (QC). Tanpa pengecekan ini maka perulangan akan menghasilkan 8 karakter (QCAAAAAA), yang mana tidak diperlukan.

Ayo coba

Potongan kode di atas dibuat dalam bentuk fungsi statik. Untuk menggunakannya, cukup panggil fungsi tersebut di kode program utama.

Console.WriteLine(GuidToBase32(Guid.Parse("15f70893-d7dd-ec11-8ea1-000d3af41650")));
// Output: teco7ku3xoeyohguaidud23cqc

Console.WriteLine(GuidToBase32(Guid.Parse("f1b92ec5-e1a3-435e-90a0-df53ec410fcb")));
// Output: fwls3ypubxxgeicu76uy6a5blg

Pada artikel berikutnya, kita akan membahas cara konversi sebaliknya dari Base32 menjadi Guid.

Tulis Komentar