כל מה שרציתם לדעת על Base64

הסיפור ההיסטורי והפרטים הטכניים של שיטת קידוד נתונים קצת משונה, שמצאה בכל זאת מקום של כבוד במערכות מודרניות.

אם הסתובבתם מספיק זמן ברשת, סביר להניח שנתקלתם פה ושם במחרוזות משונות כמו זו:

WW91IGhhdmUgd29uIDEwIGdlZWsgcG9pbnRzIQ==

הן יכולות להיות קצרות או ארוכות (ואפילו ארוכות מאוד), ולהופיע למשל באימיילים או בכתובות URL. לפעמים מדובר ב"מפתחות" לתוכנה, בקוד מזהה כלשהו או במידע חשוב אחר, שלכאורה אין סיבה שיופיע כרצף של אותיות ומספרים דווקא.

מחרוזות אלה הן למעשה מידע בינארי (כלומר כזה שיכול להיות כל דבר, לא בהכרח טקסט) שמקודד בשיטה שנקראת Base64. כמו שבבסיס 2 מייצגים כל ביט בודד באמצעות אחד משני תווים, "0" או "1", ובבסיס 16 (הקסדצימלי) מייצגים רביעיות ביטים בעזרת אחד משישה-עשר תווים, כך בבסיס 64 אנחנו מייצגים כל שישיית ביטים בנתונים המקוריים באמצעות תו אחד. לשם כך אנחנו זקוקים כמובן ל-64 תווים שונים, ואלה הם האותיות הרישיות A עד Z, האותיות הקטנות a עד z, הספרות 0 עד 9 והתווים "+" ו-"/" (בדרך כלל). אם כך, למה מופיעים בסוף המחרוזת למעלה תווי "="? נגיע לזה בהמשך, סבלנות.

במערכות מחשב ומיקרו-בקרים מודרניים, הערוצים הפנימיים, הזיכרון והחישובים מתבססים על יחידות שהן 8-כפול-שתיים-בחזקת-משהו ביטים (8, 16, 32, 64). החלוקה של יחידות אלה לבסיס 2 או 16, כלומר ליחידות של ביט אחד או ארבעה, היא קלה יחסית, ותמיד ללא שארית. בבסיס 64, לעומת זאת, מחלקים למעשה את שמיניות הביטים לשישיות, מה שישאיר לעתים קרובות "עודפים" או "זנבות" שצריך לטפל בהם. בניגוד לבסיסים הנמוכים יותר, הבסיס הזה הוא גם בלתי קריא בעליל לבני אדם. אז מי צריך אותו?

הסיבה לפיתוח Base64 הייתה היסטורית בעיקרה: המון מערכות קודדו מידע באופנים שונים, וכשהמערכות האלה התחילו לדבר זו עם זו מרחוק בשיטות שגם הן לא היו אחידות, הדבר גרם לאינספור התנגשויות ותקלות. ישראלים שהיו ברשת לפני שנים בוודאי זוכרים היטב את בעיות הקידוד, שהפכו טקסטים בעברית (באתרים ובאימיילים, למשל) לג'יבריש. בנוסף, איך מעבירים מידע בינארי אם קשה לסמוך על צירוף של קבצים, או שהוא לגמרי בלתי אפשרי (כמו בכתובות URL)?

אפשרות אחת היא להמיר כל ביט בנתונים המקוריים בתו שלם, וליצור כך מחרוזת שמקבילות לייצוג בבסיס 2. כך למשל המספר העשרוני 13 יתורגם למחרוזת "1101". כמעט כל המערכות מכירות את קוד ASCII, או לפחות תת-קבוצה סבירה שלו, ויוכלו למסור ולקבל מחרוזות כאלה ללא שיבושים. הבעיה בשיטה הזו היא שבייט יחיד של נתונים, שמכיל 8 ביטים, יהפוך למחרוזת עם שמונה בייטים (אחד לכל תו) וזה בזבוז נוראי, במיוחד כשמדובר בשידור שלוקח זמן ורוחב פס.

בניסוח כללי יותר של הבעיה, אנחנו מחפשים שיטה שתאגד מצד אחד קבוצות גדולות ככל האפשר של ביטים, ומצד שני תסתמך על אוסף תווים מצומצם שכל המערכות יכירו. כפי שבטח כבר ניחשתם, Base64 הוא התשובה. הסיכוי לשיבוש בהעברה של טקסט עם אותיות לטיניות וספרות – התווים הכי סטנדרטיים שיש – הוא נמוך ביותר, וכשכל תו מייצג שישה ביטים במקום שמונה, זה "מנפח" את השידור ב-33% בלבד.

כדי להקל על העיבוד וההמרה מ- ול-Base64, הוחלט שכל שלישיית בייטים בנתונים הגולמיים תתורגם לרביעיית תווים ב-Base64 (בשני המקרים מדובר על ייצוג של 24 ביטים). אלא שכמובן, אף אחד לא מבטיח לנו שמספר הבייטים בנתונים יתחלק בלי שארית ב-3. כדי למלא את המקומות החסרים בעת הצורך, וכדי להבהיר לצד המפענח שאלה לא נתוני אמת אלא רק ממלאי מקום, משתמשים בתו "=" או שניים, כפי שראינו במחרוזת למעלה.

וכמו שקורה לעתים קרובות מדי, שיטה שנועדה לפתור בעיות של תאימות יוצרת בעיות תאימות חדשות משל עצמה. הסתבר שהתווים "+" ו-"/" שנבחרו כחלק מהקידוד של Base64 לא התאימו לכל תסריטי השימוש. לדוגמה, אם מנסים לשים אותם בתוך כתובת URL, הדפדפן עלול לחשוב ש-"/" מייצג משהו אחר לגמרי, פרמטר של הכתובת ולא חלק מהמידע. גם מערכות קבצים מסוימות לא יודעות מה לעשות עם תווים אלה. כך נוצרו מספר תקנים, שבהם אחד מהתווים הבעייתיים או שניהם הוחלפו במקף, קו תחתון, נקודה, פסיק, נקודתיים או טילדה ("~"). חלק מהתקנים הגדילו ראש והוסיפו תווים לשבירת שורות, ויתרו על תווי ה-"=" המרפדים, צירפו קוד CRC לאיתור שגיאות, ועוד. בקיצור, כשמישהו מבקש מכם לעבוד בקידוד Base64, רצוי מאוד לשאול לאיזה Base64 בדיוק הוא מתכוון!

התכנות של קידוד ופענוח Base64 לא מובן מאליו למתחילים, וזה יכול להיות תרגיל תכנות לא רע. בשלב ראשון נזדקק לשתי פונקציות: אחת שממירה מספר (0-63) לתו המתאים בקידוד, ואחת שממירה מתו כזה בחזרה למספר המקורי. אפשר לעשות זאת באמצעות טבלת חיפוש (LUT) – הפתרון המהיר ביותר אך גם בזבזני יחסית בזיכרון, ולכן לא אידאלי למיקרו-בקרים – או באמצעות סדרה של תנאים והשוואות. המהדרין יכולים לחפש דרכים לייעל ככל האפשר את סדרת התנאים; גם שם אפשר להרוויח כמה מחזורי שעון יקרים.

כשהפונקציות האלה מוכנות ובדוקות, כותבים עוד שתיים – אחת לקידוד ואחת לפענוח. בשתיהן אנחנו בעצם צוברים ביטים מרצף אחד (נתוני האמת או מחרוזת Base64, בהתאמה) ומקבצים אותם לתוך רצף אחר, תוך התחשבות במקרי הקצה עם תווי ה-"=". יש כמה דרכים לגשת לזה: אפשר להסתמך על שלישיות/רביעיות בייטים באמצעות casting שלהם למשתני Integer עם 32 ביטים (נוח במחשבים), או לעבוד עם בייטים בודדים (מתאים למיקרו-בקרים 8 או 16 ביט). כדי ליצור ולבדוק מחרוזות שעוברות דרך התוכנה שלכם, בשלבי הפיתוח והדיבוג, רצוי מאוד להשתמש בכלי קיים שממיר מ- ול-Base64.

אם חשבתם ש-Base64 זה דבר נידח ואקזוטי, עוד לא ראיתם כלום: יש רשימה ארוכה של קידודים שמתבססים על רעיון דומה של ייצוג נתונים בעזרת תווים קריאים, אבל חלקם הולכים רחוק אף יותר ומוותרים על מספר הביטים האחיד שמיוצג בכל תו: קידוד Base36, שאולי מוכר לכם מכתובות URL "מקוצרות" מסוימות, קידוד Base85 ועוד.

להרשמה
הודע לי על
2 תגובות
מהכי חדשה
מהכי ישנה לפי הצבעות
Inline Feedbacks
הראה את כל התגובות

מעניין מאוד. תודה רבה

מעניין, תודה!