Bytes και πλάτη#

Στο τέλος αυτού του κεφαλαίου θα μπορείτε να επιλέγετε τη σωστή οδηγία ακεραίων για ένα δεδομένο πεδίο, να μετράτε τα bytes που παράγει ένα πρότυπο, και να ξεχωρίζετε πότε χρειάζεστε σταθερό πλάτος έναντι native πλάτους.

Τα δυαδικά πρότυπα ορίζονται με βάση μετρητές bytes. Το «ένα μη προσημασμένο πεδίο μήκους 16-bit ακολουθούμενο από πακέτα 4-byte» είναι μια οδηγία για bytes, όχι για αριθμούς. Η πρώτη δουλειά ενός προτύπου pack είναι να ταιριάξει με αυτούς τους μετρητές ακριβώς.

Οι οδηγίες ακεραίων σταθερού πλάτους#

Η ραχοκοκαλιά κάθε προτύπου:

Bytes

Μη προσημασμένος

Προσημασμένο

1

C

c

2

S

s

4

L

l

8

Q

q

Κάθε οδηγία καταναλώνει μία τιμή από τη λίστα και παράγει ακριβώς τόσα bytes:

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 είναι η προεπιλεγμένη οδηγία ενός byte#

Για bytes πρωτοκόλλου - πεδία σημαιών, αριθμούς εκδόσεων, opcodes, ετικέτες τύπου - το C (μη προσημασμένο 8-bit, από 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 bytes ανεξάρτητα από τον μεταγλωττιστή C. Τα native-width αντίστοιχά τους προσθέτουν !:

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-bit). Το i! είναι ψευδώνυμο του i.

Πρακτικός κανόνας:

  • Για διαλειτουργικά δυαδικά (μορφές επί της γραμμής, αρχεία σε δίσκο, δεδομένα που διασχίζουν μηχανές) χρησιμοποιήστε σταθερό πλάτος (l, L, …) συν ρητό endianness - ή τις φορητές συντομεύσεις.

  • Για εργασία εντός διεργασίας που ταιριάζει σε κάποιο τοπικό struct της C που πρόκειται να παραδώσετε στο ioctl ή στο syscall, χρησιμοποιήστε native πλάτος (l!, L!, i, I).

Η μείξη των δύο είναι αποδεκτή, αλλά σταματήστε και ρωτήστε τον εαυτό σας γιατί κάθε φορά.

Μέτρηση του μήκους ενός προτύπου#

Πριν στείλετε μια εγγραφή, ελέγξτε ότι ο μετρητής bytes είναι αυτός που λένε οι προδιαγραφές. Η length pack(TEMPLATE, ...) με placeholder τιμές είναι ο απλούστερος τρόπος:

my $sz = length pack "n N L C", 0, 0, 0, 0;
# 2 + 4 + 4 + 1 = 11

Για πρότυπα των οποίων το μήκος εξαρτάται από native μεγέθη, η length pack TEMPLATE, 0, 0, ... σας δίνει την απάντηση στην τρέχουσα μηχανή.

Επεξεργασμένο παράδειγμα: ένα ελάχιστο header πακέτου#

Ένα υποθετικό πρωτόκολλο ορίζει:

  • έκδοση 8-bit

  • σημαίες 8-bit

  • μήκος 16-bit big-endian (ακολουθεί φορτίο)

Τρία πεδία, τέσσερα bytes συνολικά. Το πρότυπο γράφεται από μόνο του:

my $hdr = pack "C C n", $version, $flags, length $payload;
my $pkt = $hdr . $payload;

length $hdr    # 4

Δύο πράγματα προς προσοχή:

  1. Δεν γράψαμε αναλυτικά C1 C1 n1 - ένα γυμνό γράμμα οδηγίας έχει υπονοούμενο μετρητή επανάληψης 1.

  2. Το μήκος στο τρίτο πεδίο υπολογίζεται από το $payload στην ίδια έκφραση. Ένα μεταγενέστερο κεφάλαιο δείχνει τη μορφή / που συνδέει τα δύο μεταξύ τους ώστε να μη μπορείτε να ξεχάσετε.

Οριακές περιπτώσεις άξιες να θυμάστε#

  • Τιμές εκτός εύρους: η pack "C", 256 αποκόπτει σιωπηλά (παίρνετε \x00). Δεν υπάρχει προειδοποίηση. Ελέγξτε τα εύρη οι ίδιοι αν τα δεδομένα δεν είναι αξιόπιστα.

  • Πλήρης κύκλος προσημασμένου έναντι μη προσημασμένου: μια τιμή πακεταρισμένη ως C και αποπακεταρισμένη ως c (ή το αντίστροφο) αλλάζει πρόσημο για τιμές ≥ 128:

    unpack "c", pack "C", 200        # -56
    
  • Πολύ λίγες τιμές: οι τιμές που λείπουν πακετάρονται ως 0 για αριθμητικές οδηγίες, "" για συμβολοσειρές. Καμία προειδοποίηση.

Το επόμενο κεφάλαιο χειρίζεται το ερώτημα που εγείρει κάθε ακέραιος πολλαπλών bytes: ποια άκρη του αριθμού πάει πρώτη;