בתים ורוחבים#
בסוף הפרק הזה תוכלו לבחור את הוראת המספר השלם הנכונה לשדה נתון, לספור את הבתים שתבנית מייצרת, ולקבוע מתי צריך רוחב קבוע לעומת רוחב native.
פורמטים בינאריים מוגדרים לפי מוני בתים. ״שדה אורך 16-ביט ללא־סימן ולאחריו packets של 4 בתים״ הוא הוראה על בתים, לא על מספרים. המשימה הראשונה של תבנית pack היא להתאים בדיוק את המונים האלה.
הוראות המספר השלם ברוחב קבוע#
עמוד השדרה של כל תבנית:
בתים | ללא־סימן | עם סימן |
|---|---|---|
1 |
|
|
2 |
|
|
4 |
|
|
8 |
|
|
כל הוראה צורכת ערך אחד מהרשימה ומייצרת בדיוק את מספר הבתים הזה:
length pack "C", 65 # 1
length pack "S", 12345 # 2
length pack "L", 1_000_000_000 # 4
length pack "Q", 1_000_000_000_000 # 8 (if 64-bit Perl)
מונה חזרות אורז את מספר ההוראות הזהות הזה ברצף:
length pack "C4", 1, 2, 3, 4 # 4
length pack "L3", 10, 20, 30 # 12
הרכיבו אותם בחופשיות; האורך הכולל הוא הסכום:
length pack "C S L", 65, 1000, 2_000_000 # 1 + 2 + 4 = 7
C היא הוראת ברירת המחדל של בית אחד#
לבתים של פרוטוקול - שדות דגלים, מספרי גרסה, opcodes, תגי סוג - C (8-ביט ללא־סימן, 0 עד 255) היא כמעט תמיד הבחירה הנכונה. c (עם סימן, −128 עד 127) מופיעה רק כאשר המפרט אומר ״signed״. W הוא וריאנט של C שמאפשר ערכים מעל 255 כאשר התבנית שלכם במצב U0 (UTF-8) - התעלמו ממנו עד שתיתקלו במקרה הזה.
כתובות IP הן הדוגמה מספר הלימוד:
my $raw = pack "C4", 192, 168, 1, 42;
# ^-- four unsigned bytes, in the order the list gives them
רוחב native מול רוחב קבוע#
ההוראות s / S / l / L מוגדרות להיות בדיוק 2 ו־4 בתים ללא תלות במהדר C. המקבילות שלהן ברוחב native מוסיפות !:
length pack "l", 0 # 4 (always)
length pack "l!", 0 # 4 on most 32-bit and 64-bit systems,
# 8 on 64-bit Alpha, some legacy Unixes.
i ו־I הן תמיד native (מה ש־sizeof(int) מחזיר על המכונה הזו, עם מינימום של 32-ביט). i! הוא alias עבור i.
כלל אצבע:
לבינארי בר־פעולה הדדית (פורמטים של חוט, קבצים על דיסק, נתונים החוצים בין מכונות) השתמשו ב־רוחב קבוע (
l,L, …) בתוספת endianness מפורש - או ב־קיצורים הניידים.לעבודה בתוך התהליך התואמת struct של C מקומי שאתם עומדים למסור ל־
ioctlאו ל־syscall, השתמשו ב־רוחב native (l!,L!,i,I).
ערבוב בין השניים הוא תקין, אך עצרו ושאלו את עצמכם למה בכל פעם.
ספירת אורך התבנית#
לפני שליחת רשומה, בדקו שמונה הבתים הוא מה שהמפרט אומר. length pack(TEMPLATE, ...) עם ערכי מציין־מקום היא הדרך הפשוטה ביותר:
my $sz = length pack "n N L C", 0, 0, 0, 0;
# 2 + 4 + 4 + 1 = 11
לתבניות שאורכן תלוי בגדלי native, length pack TEMPLATE, 0, 0, ... נותן את התשובה על המכונה הנוכחית.
דוגמה מפותחת: כותרת packet מינימלית#
פרוטוקול היפותטי מגדיר:
גרסה 8-ביט
דגלים 8-ביט
אורך 16-ביט big-endian (אחריו payload)
שלושה שדות, ארבעה בתים בסך הכל. התבנית כותבת את עצמה:
my $hdr = pack "C C n", $version, $flags, length $payload;
my $pkt = $hdr . $payload;
length $hdr # 4
שני דברים שכדאי לשים לב אליהם:
לא איייתנו
C1 C1 n1- לאות הוראה חשופה יש מונה חזרות מובלע של 1.האורך בשדה השלישי מחושב מ־
$payloadבאותו ביטוי. פרק מאוחר יותר מראה את צורת ה־/הקושרת את השניים יחד כדי שלא תוכלו לשכוח.
מקרי קצה ששווה לזכור#
ערכים מחוץ לטווח:
pack "C", 256חותך בשקט (מקבלים\x00). אין אזהרה. בדקו טווחים בעצמכם אם הנתונים אינם מהימנים.נסיעה חזור עם סימן מול ללא־סימן: ערך הנארז כ־
Cומפוענח כ־c(או להפך) הופך סימן עבור ערכים ≥ 128:unpack "c", pack "C", 200 # -56
מעט מדי ערכים: ערכים חסרים נארזים כ־
0עבור הוראות מספריות,""עבור מחרוזות. אין אזהרה.
הפרק הבא מטפל בשאלה שכל מספר שלם רב־בתי מעלה: איזה קצה של המספר בא ראשון?