"Sputnik" help  
Sputnik Help
Pack(<formatString>, <packingVariables...>)

Description

The Pack function is used to convert variables into a binary variable (a string of bytes often called a binary string) according to a specified format. It allows you to pack variables of different types, such as integers, floats, strings, and more, into a compact binary representation.

By providing a format string as the first argument and the corresponding packing variables as the following arguments, you can define how each variable should be packed and arranged in the resulting binary string.

The format string consists of format specifiers that define the type and arrangement of the packing variables. Each format specifier represents a specific data type and determines the number of bytes used for packing. For example, the format specifier "i" represents a signed integer, while "f" represents a floating-point number.

The packing variables can be scalar values, arrays, or a combination of both. If an array is provided, it will be unraveled into individual packing variables, allowing you to pack multiple elements from the array into the binary string.

The Pack function returns the binary string representing the packed data. If the packing operation is successful and the provided format and variables are valid, the function will return the packed data. However, if there are any errors or issues during the packing process, an empty binary string may be returned.

To handle potential errors, you can check the length of the returned binary string. If the length is zero, it indicates that the packing operation did not produce any data, possibly due to an error. In such cases, appropriate error handling or validation mechanisms should be implemented.

Overall, the Pack function provides a flexible and powerful way to convert variables into a binary representation, allowing for efficient storage, transmission, and manipulation of data.


Format Specifier

A format specifier is a string pattern used in the Pack function to define how data should be packed into a binary format. It consists of format codes that specify the type and size of the data to be packed.

Each format code represents a specific data type and has a corresponding meaning. The format specifier string can contain multiple format codes, each one indicating how to pack a different piece of data. The order of the format codes in the string should match the order of the arguments provided to the Pack function.

For example, the format code "a5" specifies that the next argument should be packed as a string of length 5. The format code "i" indicates that the next argument should be packed as a signed integer, and so on.

Here are a few common format codes:

"a": Pack a string (null-padded)
"A": Pack a string (space-padded)
"c": Pack a signed char
"C": Pack an unsigned char
"s": Pack a signed short (16-bit)
"S": Pack an unsigned short (16-bit)
"i": Pack a signed integer (32-bit)
"I": Pack an unsigned integer (32-bit)
"l": Pack a signed long (32 or 64-bit, depending on the platform)
"L": Pack an unsigned long (32 or 64-bit)
"f": Pack a float (single-precision)
"d": Pack a double (double-precision)
In addition to the basic format codes, you can also use modifiers to specify the size and endianness of the packed data. For example, "n" specifies a short (16-bit) in network byte order (big-endian), and "v" specifies a short in little-endian byte order.

Here is the complete listing of format specifiers:
(Bare in mind these format specifiers are specific to Pack make sure to read Unpack to get its specifiers)

a  - NUL-padded ASCII string
A - SPACE-padded ASCII string
Z - NUL-padded ASCII string with NUL-terminator
u - UUEncoded string
b - Bit string (ascending bit order inside each byte)
B - Bit string (descending bit order inside each byte)
h - Hex string, low nibble first
H - Hex string, high nibble first
c - Signed ASCII char
C - Unsigned ASCII char
U - Signed Unicode char (16-bit, machine byte order)
W - Unsigned Unicode char (16-bit, machine byte order)
G - Signed short (always 16-bit, big endian byte order)
g - Signed short (always 16-bit, little endian byte order)
s - Signed short (16-bit, machine byte order)
S - Unsigned short (16-bit, machine byte order)
n - Unsigned short (16-bit, big endian byte order)
v - Unsigned short (16-bit, little endian byte order)
i - Signed integer (32-bit, machine dependent size and byte order)
I - Unsigned integer (32-bit, machine dependent size and byte order)
E - Signed long (always 32-bit, big endian byte order)
e - Signed long (always 32-bit, little endian byte order)
l - Signed long (32-bit, machine byte order)
L - Unsigned long (32-bit, machine byte order)
q - Signed quad (64-bit) value (always 64-bit, machine byte order)
Q - Unsigned quad (64-bit) value (always 64-bit, machine byte order)
N - Unsigned long (32-bit, big endian byte order)
V - Unsigned long (32-bit, little endian byte order)
f - Float (machine dependent size and representation)
d - Double (machine dependent size and representation)
p - Pointer to a null-terminated string
P - Pointer to a fixed-length string
t - Signed pointer
T - Unsigned pointer
z7 - String encoded as UTF8 with 1-byte null terminator
z6 - String encoded as UTF7 with 1-byte null terminator
z5 - String encoded as UTF16 with 2-byte null terminator
z4 - String encoded as BigEndianUnicode with 2-byte null terminator
z3 - String encoded as UTF32 big endian with 4-byte null terminator
z2 - String encoded as UTF32 with 4-byte null terminator
z1 - String encoded as ASCII with 1-byte null terminator
z0 - String encoded as ASCII without a null terminator
x - NUL byte
X - Back up one byte
@ - NUL-fill to absolute position

Note: In Sputnik, the Pack function provides flexibility in matching format specifiers with the actual packing variables. Sputnik dynamically converts the variables to match the specified format specifier, even if they do not match in their original form.

For example, you can pack a string representation of an integer, such as "777," using the format specifier "i" for integer. Sputnik will automatically convert the string to an integer during the packing process, ensuring compatibility between the format specifier and the variable.

While this dynamic conversion feature can be convenient, it is essential to ensure that the chosen format specifier accurately represents the intended data type and format of the packed variable. Using incompatible or incorrect format specifiers may lead to unexpected results or errors during the packing process.

Therefore, it is recommended to use format specifiers that best match the intended data type and adhere to proper data conversion practices. This ensures that the packed binary data reflects the desired format and can be correctly interpreted during subsequent unpacking or processing operations.

By understanding the dynamic conversion capabilities of Sputnik and selecting appropriate format specifiers, you can effectively utilize the Pack function to pack variables of different types into binary representations, accommodating the dynamic nature of the language and enabling versatile data manipulation tasks.

Parameters

<formatString> A string specifying the format of the packing. It consists of format specifiers that define how each variable should be packed.
<packingVariables...> One or more variables or values to be packed into binary data according to the format string. These can be scalars, arrays, or a combination of both. If an array is provided, its elements will be unraveled into individual packing variables. Each packing variable will be interpreted based on the corresponding format specifier in the format string.

Return Value

Success: Returns the packed binary data as a binary string if the packing operation was successful.
Failure: Returns empty binary data if an error occurred during the packing process.

Remarks

In Sputnik, the return value of the Pack() function is a binary variable (often called a binary string) which is similar to an array of numbers but fixed in size and each number can only range from 0-255. While it is not a string, you can still interpret it as a string by using casting or printing it.

If you want to treat the binary variable as a Unicode string instead of ASCII, you can use the BinaryFromStr() function. This function explicitly converts the binary data to a string, allowing you to work with Unicode characters.

In Perl, the Pack() and Unpack() functions directly read from and write to strings. In Sputnik, the Pack() function produces a binary variable, representing an array of bytes. You can manipulate this binary variable using Binary___() functions and cast it to a string if desired. Casting it as (string) will treat it as an ASCII string, while using BinaryFromStr() allows you to specify Unicode encoding.

It's important to note that Sputnik stores variables as signed or unsigned as needed. When the format specifier indicates an unsigned value, Sputnik treats the variable as specified in both packing and unpacking operations.

Additionally, when packing multiple pieces of data, not providing a name for an element results in an empty string as the key. If only one element is named, it overwrites data as the keys are the same.

A word of caution: the * character in the format specifier only means "consume all of the current piece of data." Therefore, using "AA" will pack all of $one into the first A* and all of $two into the second A*. Each format character corresponds to one piece of data to be packed.

Overall, Sputnik's Pack() function provides flexibility in handling binary data and allows for efficient and concise packing and unpacking operations.

Related

PackSingle, Unpack, UnpackSingle, Bin

Example

Example of choosing names for Key in the return array:

my $binaryData = "\x04\x00\xa0\x00";
my $array = Unpack("cchars/nint", $binaryData);
// The resulting array will contain the entries
// "chars" with value 4 and "int" with 160. 
printr $array;
// Prints:
// Array
// (
//         [chars] => 4
//         [int] => 194
// )

This time it will assign the key then prefix 1/2 etc etc as it finds each element:

my $binaryData = "\x04\x00\xa0\x00";
my $array = Unpack("c2chars/nint", $binaryData);
// The resulting array will contain the entries
// "chars1", "chars2" and "int". 
printr $array;
// Prints:
// ARRAY
// {
//         [chars1] => 4
//         [chars2] => 0
//         [int] => 49824
// }

Packing a bunch of values at once:

my $binaryData = Pack("nvc*", 0x1234, 0x5678, 65, 66);
// The resulting binary string will be 6 bytes long
// and contain the byte sequence 0x12, 0x34, 0x78, 0x56, 0x41, 0x42. 
printr $binarydata;
// Prints:
// Binary
// (
//     [0] => 0x12 [18] (.)
//     [1] => 0x34 [52] (4)
//     [2] => 0x78 [120] (x)
//     [3] => 0x56 [86] (V)
//     [4] => 0x41 [65] (A)
//     [5] => 0x42 [66] (B)
// )

Using * to pack all the remaining objects with the same specifier:

printr pack("C*", 'S', 'P', 'K');
// PRINTS
// Binary
// (
//     [0] => 0x53 [83] (S)
//     [1] => 0x50 [80] (P)
//     [2] => 0x4B [75] (K)
// )

Display the ASCII character codes for an entire string using Unpack():

say join (unpack("C*", "abcdef"), " ");
// 97 98 99 100 101 102

Display the UNICODE character codes for an entire stringusing Unpack():

my $Str = "こんにちは";
say join (unpack("U*", pack("U*", split($Str, ""))), " ");
// 12371 12435 12395 12385 12399

String to Hex and back again:

function H2Str( $hex ) 
{
    return pack("H*", $hex);
} 
function Str2H( $str )
{
    return unpack("H*", $str, @UnpackSingle);
}
my $txt = "This is test";
my $hex = Str2H($txt);
my $str = H2Str($hex);
say "${txt} => ${hex} => ${str}";
// PRINTS
// This is test => 546869732069732074657374 => This is test

Convert a double into a binary array and back again:

my $arr = Pack("d", 777.42);
my $d = Unpack("d", $arr, @UnpackSingle);
say $d;
// PRINTS
// 777.42

Convert an int into a binary array and back again:

my $arr = Pack("i", 31337);
my $d = Unpack("i", $arr, @UnpackSingle);
say $d;
// PRINTS
// 31337

Convert a string into a hex and back again:

my $str = "Hello World!";
say "Original String: " . $str;
my $hex = Unpack("H*", $str, @UnpackSingle);
say "Hex String: " . $hex;
my $strf = Pack("H*", $hex);
say "Normal String: " . $strf;
// PRINTS
// Original String: Hello World!
// Hex String: 48656C6C6F20576F726C6421
// Normal String: Hello World!

Convert a string into a hex and back again (using a bit of Regexp too):

Function StrToHex ($str)
{
    $str = unpack("H*", $str, @UnpackSingle);
    // Operationally convert the hex characters
    // to upper case
    $Str =~ tr/a-z/A-Z/;
    return $Str;
}
Function HexToStr ($Hex)
{
    $Hex =~ s/([\dA-Fa-f][\dA-Fa-f])/pack("C", dec($1))/eg;
    return $Hex;
}
my $Hex = StrToHex("Hello world!");
say "Hex: $Hex";
my $Str = HexToStr($Hex);
say "Str: $Str";
// PRINTS
// Hex: 48656C6C6F20776F726C6421
// Str: Hello world!

Using pack to check if the system is Little-Endian or Big-Endian:

// A hex number that may represent "abyz"
my $abyz = 0x6162797A;
// Convert $abyz to a binary string containing 32 bits
// Do the conversion the way that the system architecture wants to
switch (pack ("L", $abyz))
{
    // Compare the value to the same value converted in a Little-Endian fashion
    case pack ("V", $abyz):
        say "Your system is Little-Endian.";
        break;
    // Compare the value to the same value converted in a Big-Endian fashion
    case pack ("N", $abyz):
        say "Your system is Big-Endian.";
        break;
    default:
        say "Your system \"endian\" is unknown.";
        break;
}

Using Unpack() to do a simplistic SubStr() on an ASCII string:

Function _SubStr ( $what, $where, $howmuch )
{
    return unpack("x$where/a$howmuch", $what);
}
printr _SubStr("UberFoX", 2, 3);
// PRINTS
// erF

Example of packing multiple ASCII strings:

my $bin = Pack("z1z1z1", "Cat", "Dog", "Fox");
my $unpacked = Unpack("z1/z1/z1", $bin);
printr $unpacked;
// PRINTS
// Array
// (
//     [0] => Cat
//     [1] => Dog
//     [2] => Fox
// )

Example of packing multiple ASCII strings and unpacking them by name:

my $name = "UberFoX";
my $id = "yay?";
my $randomText = "Hello world!";
my $bin = Pack("z1z1z1", $name, $id, $randomText);
my $unpacked = Unpack("z1Name/z1Id/z1RandomText", $bin);
printr $unpacked;
// PRINTS
// Array
// (
//     [Name] => UberFoX
//     [Id] => yay?
//     [RandomText] => Hello world!
// )

Example of packing multiple ASCII strings and unpacking them using the List statement:

my $name = "UberFoX";
my $id = "yay?";
my $randomText = "Hello world!";
my $bin = Pack("z1z1z1", $name, $id, $randomText);
List ($vName, $vId, $vRandomText) = Unpack("z1/z1/z1", $bin);
say "Name is: $vName";
say "ID is: $vId";
say "RandomText is: $vRandomText";
// Print
// Name is: UberFoX
// ID is: yay?
// RandomText is: Hello world!

Same as above but why stop at just text? Lets add an Int and Float in there:

my $name = "UberFoX";
my $id = "yay?";
my $randomText = "Hello world!";
my $randomInt = 7000;
my $randomFloat = 133.7;
my $bin = Pack("z1iz1z1d", $name, $randomInt, $id, $randomText, $randomFloat);
List ($vName, $vInt, $vId, $vRandomText, $vFloat) = Unpack("z1/i/z1/z1/d", $bin);
say "Name is: $vName";
say "Id is: $vId";
say "RandomText is: $vRandomText";
say "Int is: $vInt";
say "Float is: $vFloat";
// PRINTS
// Name is: UberFoX
// Id is: yay?
// RandomText is: Hello world!
// Int is: 7000
// Float is: 133.7

Example of using UUEncode:

//This will read/write the string as it is in Sputnik so
// it handles Unicode/ASCII just fine no problems
my $enc = pack("u", "Testy");
print "Value Encoded '$enc'\n";
my $dec = unpack("u", $enc, 3);
print "Value Decoded '$dec'\n";
// Prints
// Value Encoded '%5&5S='D`
// '
// Value Decoded 'Testy'

Example of using UUEncode multiple times with other data as well:

// Encode some text to UUEncode and add some integers and floats 
// in for the fun of it
my $enc = pack("uiud", "Testy", 777, "Cat", 133.77);
print "Value Encoded '$enc'\n";
my $dec = unpack("u/i/u/d", $enc);
print "Value Decoded:\n";
printr $dec;
// PRINTS
// Value Encoded '%5&5S='D`
//         #0V%T
// q=
// ×£¸`@'
// Value Decoded:
// Array
// (
//     [0] => Testy
//     [1] => 777
//     [2] => Cat
//     [3] => 133.77
// )

Example of using a pointer to a null-terminated string:

// Allocate memory for an binary string of 4 elements
my $p = Alloc(4);

// Assign the characters 'C', 'a', and 't' to the binary string
$p[0]:C = 'C';
$p[1]:C = 'a';
$p[2]:C = 't';

// Create a binary buffer with a capacity of probably 8 bytes
my $bin = BinNew(@IntPtrSize);

// Write the memory address of the allocated memory to the binary buffer
BinaryWriteUIntPtr($bin, 0, $p);

// Unpack a pointer value from the allocated memory
my $str = Unpack("p", $bin, 3);

// Print the unpacked value
say "Value is: $str";

// Free the allocated memory
free($p);

// PRINTS
// Cat

This code snippet allocates memory for an array and assigns the characters 'C', 'a', and 't' to its memory locations. It creates a binary buffer, writes the memory address of the array to the buffer, and then unpacks a pointer value from the buffer. The unpacked value is stored in $str, which is then printed as "Cat". Finally, the allocated memory is freed using the free() function.

Example of using an array with an array embedded as the argument:

my $array = array("Cat", "Dog", "FoX", array("hmm", "lol"));
my $ret = pack("A*A*A*A*A*", $array);
print "$ret\n";
// PRINTS
// CatDogFoXhmmlol

Same as above but this time with normal arguments along side arrays:

my $array = array( "Cat", "Dog", "FoX", array( "hmm", "lol" )  );
my $ret = pack("A*A*A*A*A*A*A*", "hello", $array, "world!");
print "$ret\n";
// Prints
// HelloCatDogFoXhmmlolworld!

To pack an array of Integers to binary you would do:

// Of course this would work with anything not just Integers
 
// Make array of Integers
my $array = array( 100, 200, 300, 400, 500, 600  );
my $count = Count($array);
// Pack the array of Integers
my $bin = pack("I$count", $array);
// Unpack back to an array of Integers
my $newArray = unpack("I$count", $bin);
printr $newArray;
// PRINTS
// Array
// (
//         [0] => 100
//         [1] => 200
//         [2] => 300
//         [3] => 400
//         [4] => 500
//         [5] => 600
// )
// This time lets unpack it to a named list
// We could unpack more but lets just get the first 3
my List ($int0, $int1, $int2) = unpack("I3", $bin);
say "Int0 = $int0 | Int1 = $int1 | Int2 = $int2";
// PRINTS
// Int0 = 100 | Int1 = 200 | Int2 = 300

Reading and writing ASCII:

my $vec = pack("A*", "Hello");
printr $vec;
printr unpack("A*", $vec, @UnpackSingle);
// PRINTS
// Binary
// (
//     [0] => 0x48 [72] (H)
//     [1] => 0x65 [101] (e)
//     [2] => 0x6C [108] (l)
//     [3] => 0x6C [108] (l)
//     [4] => 0x6F [111] (o)
// )
// Hello

Reading and writing ASCII in bits:

my $vec = pack("A*", "Hello");
my $chars = unpack("A/A/A2", $vec, @UnpackArray);
print("First letter: " . $chars[0] . "\n");
print("Second letter: " . $chars[1] . "\n");
print("Third and forth letter: " . $chars[2] . "\n");
// Prints
// First letter: H
// Second letter: e
// Third and forth letter: ll

Format ASCII text in a readable way with everything correctly spaced out:

// Make an array of people
my $People = array(array("Name", "ID", "Birth", "Sex", "Race"));
push($People, array("Mike", 0, "1980", "M", "German"));
push($People, array("Tommy", 1, "1985", "M", "French"));
push($People, array("David", 2, "1950", "M", "Jewish"));
push($People, array("Sukara", 3, "1990", "F", "Japanese"));
push($People, array("Mary-Kate", 4, "1987", "F", "American"));
 
// Print them all in a nice formatted way
foreach(my $Person in $People)
{
    my List ($Name, $ID, $Birth, $Sex, $Race) = $Person;
    my $Sorted = pack("A12 A4 A7 A5 A*", $Name, $ID, $Birth, $Sex, $Race);
    say "$Sorted";
}
// Prints
// Name        ID  Birth  Sex  Race
// Mike        0   1980   M    German
// Tommy       1   1985   M    French
// David       2   1950   M    Jewish
// Sukara      3   1990   F    Japanese
// Mary-Kate   4   1987   F    American

Read from formatted ASCII text and extract information from it similar to Regex but much more reliable for this specific task:

// Lets imagine this variable was loaded from a file
// It contains information that is using specific spaces
// to keep it organized so it can be displayed on the
// company computers properly
// We are going to read this information and process it
// You could use regexp or splitting but they would be
// impractical so lets use Unpack() instead
my $data = 
            "Date      |Description                | Income|Expenditure\n"
            "01/24/2001 Zed's Camel Emporium                    1147.99\n"
            "01/28/2001 Flea spray                              24.99\n"
            "01/29/2001 Camel rides to tourists      235.00\n"
            "01/29/2001 Tourist camel feedings       100.10\n";
my $TotalIncome = 0.0;
my $TotalExpenditure = 0.0;
foreach(my $line in Lines($data))
{
    if(IsEmpty(trim($line)))
        continue; // Skip useless entries
    my List( $date, $desc, $income, $expend  ) = unpack("A10/x/A27/x/A7/A*", $line);
    $income = trim($income); // Trim it so we can do arithmetics on it
    $expend = trim($expend); // Trim it so we can do arithmetics on it
    $TotalIncome += $income;
    $TotalExpenditure += $expend;
    // Print it just so prove we are reading it correctly other than that
    // it is pointless
    say "Log: '$date' | '$desc' | '$income' | '$expend'";
}
say "Total income is: $TotalIncome";
say "Total expenditure is: $TotalExpenditure";
// Prints:
// Log: 'Date' | 'Description' | 'Income' | '|Expenditure'
// Log: '01/24/2001' | 'Zed's Camel Emporium' | '' | '1147.99'
// Log: '01/28/2001' | 'Flea spray' | '' | '24.99'
// Log: '01/29/2001' | 'Camel rides to tourists' | '235.00' | ''
// Log: '01/29/2001' | 'Tourist camel feedings' | '100.10' | ''
// Total income is: 335.1

Heres an example where we define the number to extract from an input:

printr Pack("A5", "The quick brown fox");
// PRINTS
// Binary
// (
//     [0] => 0x54 [84] (T)
//     [1] => 0x68 [104] (h)
//     [2] => 0x65 [101] (e)
//     [3] => 0x20 [32] ( )
//     [4] => 0x71 [113] (q)
// )
In the format specifier "A5", the letter "A" denotes a string with a fixed length, and the number "5" specifies the exact length of the string. Therefore, it expects the input string to be exactly 5 characters long.

When the input string "The quick brown fox" is packed with the format specifier "A5", the packing operation will take the first 5 characters from the input string and convert them into their corresponding binary representations.

Each element in the binary array represents the binary value of a single character from the input string. The hexadecimal values in square brackets represent the ASCII values of the corresponding characters. For example, [0] => 0x54 [84] corresponds to the character "T" with an ASCII value of 84.

Since the input string is longer than the specified length of 5 characters, only the first 5 characters are packed into the binary array. The remaining characters are not included in the result.


Contact
Cheryl (uberfox@hotmail.com)
Homepage
http://ubersoft.org