קבצי תצורה ו - Liboff

הפורמט הכי נפוץ כיום לקבצי תצורה (configuration) הוא json. ומסיבה טובה: קל מאוד לעבוד איתו הרבה מאוד שפות תכנות כמו: Python, Go תומכות בו ישר מהקופסא וזה מעולה. הבעיה התחילה כאשר ניסיתי לשלב כלי שכתבתי ב - C עם json.

ל - C יש כמה ספריות מעולות לניתוח json והספריה שבה אני השתמשתי הייתה jsonsl שגיליתי במקרה בעת מיפוי הקוד של Libtransmission הספרייה ממומשת כמחסנית (stack) ונראתה מתאימה. מה שהציק לי בעת הפיתוח היה כמות הקוד שנדרש בכדי לבצע פעולת פשוטות על קבצי json. הייתי צריך לכתוב 50~ LOC בכדי לפרסר קובץ json שנראה כך:

{
    "server" : "0.0.0.0",
    "port" : 4000,
    "fts"   : true,
    "cr" : ['user', 'password']
}


הקוד שכתבתי לא השתמש בתחביר json מסובך, והחלטתי לכתוב פורמט חדש שיתאים לי. במהלך התכנון שמתי לעצמי מטרה: ליצור פורמט שיהיה קריא (for humans), וקל ללמידה. לא באתי להמציא את הגלגל כמובן, רבים וטובים עשו זאת לפני. פשוט רציתי משהו שיתאים לי, יהיה קל משקל, ועדין יתמוך במערכים, מספרים שלמים והערות.
בתחילת אוגוסט השנה שחררתי את הקוד של Liboff. רוב הפידבקים שקיבלתי היו חיובים ומישהו אפילו הזכיר שהפורמט נראה כמו שילוב של yaml + json. וזה נכון כי ב - json אין אפשרות לכתוב הערות בקובץ (מה שהיה מאוד חסר לי) והתחביר של yaml קריא מאוד אז off - זה למעשה שילוב של שניהם.

מאחר ואני פריק של אופטימיזציה הספרייה Liboff נכתבה ללא שימוש בהקצאת זכרון דינמית (אפילו לא buffers). במקום זה אני משתמש ב - token על מנת לחלץ את הערכים:

typedef struct {
  int start; /* start index */
  int end; /* end index */
  offtype_t type; /* type of the token see above OT_* */

} offtok_t;


כל שורה בקובץ נראית כך:

key value


בחרתי להשתמש ב - “0x20” על מנת להפריד את המפתח מהערך. כך השרווח הראשון בשורה מוגדר כמפריד הערך. לאחר הניתוח הספרייה מנסה לנחש את סוג הערך. אם לדוגמא הערך יתחיל ב - { ויגמר ב - } הספרייה תזהה את הערך כמערך, ותגדיר את ה - index לתחילת וסיום המערך (start/end) לאחר שיש לנו את מיקום המערך, נוכל לחלץ אותו בפשטות:

printf("key start: %d, end: %d\n", l.k.start, l.k.end);
printf("value start: %d, end: %d\n", l.v.start, l.v.end);
printf("key  : %.*s\n", l.k.end - l.k.start, line + l.k.start);
printf("value: %.*s\n", l.v.end - l.v.start, line + l.v.start);


הפונקצייה שכתבתי לחילוץ מערכים קצת יותר מסובכת אבל עדיין בשיטה הזאת אנחנו לא צריכים להקצות זכרון בכלל!
התחביר המשופר של הדוגמא הראשונה:

;;
;; Example configuration file
;; Author: bindh3x
;; Date: 01/09/2017
;;
server 0.0.0.0
port 4000
fts true
cr {user|password}


יותר קריא לפי דעתי (:

קצת על - Off

Off - קיצור של Ois File Format.
ois זה שם התכנה שבשבילה כתבתי את Off. במהלך התכנון כתבתי את הספרייה Liboff שנעודה לתת ממשק לעבודה עם קבצי off מ - C/++C. אתם מוזמנים לעבור על הקוד של הספרייה ועל התיעוד המלא כאן (הוסר).

מה הלאה

כבר התחלתי לכתוב מימוש ל - Python ו - Lua. נשאר רק Go, PHP ו - Ruby שכנראה יתבצע בעתיד. אתם מוזמנים לקחת חלק בפרויקט ב - Github או לשלוח לי patch.

לסיכום:

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