"Sputnik" help  
Sputnik Help
Language Reference - Datatypes - Structs

Structs

Structs in Sputnik are binary blobs that can be used to store and manipulate structured data. They are represented as binary strings and provide a way to work with low-level binary data. Structs are useful for storing and manipulating primitive data types, such as integers, floats and even strings, in a binary format.

This is mainly for interoperability with C data types and structures, as well as low-level binary management. These additions provide a more comprehensive translation layer on top of Sputnik, enabling seamless integration with C APIs and more efficient handling of binary data.


Creating Structs

To create a struct, you define its structure using a specific syntax. The struct definition specifies the fields and their types, similar to C struct syntax. You can also specify alignments and packing options to control memory usage.

Heres a basic example of defining a struct and creating instances of it:

my $type = Struct(q~
    struct {
    char_t title[50];
    char_t author[50];
    char_t subject[100];
    uint32_t book_id;
    } Books;
~);

/* Declare books and do specification */
my $Book1 = StructInst($type, "Sputnik Programming", "Moo Moo", "Telecom Programming Tutorial", UniqueId() % @UInt32Max);
my $Book2 = StructInst($type, "Netscape Failure", "CatDog", "Netscape Failure in a NutShell", UniqueId() % @UInt32Max);

/* print Book1 info */
printf("Book 1 title : %s\n", VecEx($Book1, StructMember($type, "title")));
printf("Book 1 author : %s\n", VecEx($Book1, StructMember($type, "author")));
printf("Book 1 subject : %s\n", VecEx($Book1, StructMember($type, "subject")));
printf("Book 1 book_id : %u\n", VecEx($Book1, StructMember($type, "book_id")));

/* print Book2 info */
printf("Book 2 title : %s\n", VecEx($Book2, StructMember($type, "title")));
printf("Book 2 author : %s\n", VecEx($Book2, StructMember($type, "author")));
printf("Book 2 subject : %s\n", VecEx($Book2, StructMember($type, "subject")));
printf("Book 2 book_id : %u\n", VecEx($Book2, StructMember($type, "book_id")));

// PRINTS
// Book 1 title : Sputnik Programming
// Book 1 author : Moo Moo
// Book 1 subject : Telecom Programming Tutorial
// Book 1 book_id : 2249494843
// Book 2 title : Netscape Failure
// Book 2 author : CatDog
// Book 2 subject : Netscape Failure in a NutShell
// Book 2 book_id : 1873746678

In the given example, a struct type called "Books" is defined with fields for title, author, subject, and book_id. StructInst() is used to create instances of this struct with specified field values. The VecEx() function/operator is then used to access and print the values of the struct members for Book 1 and Book 2.

The printed output displays the information for both books, including the title, author, subject, and book_id. The Struct system simplifies the handling of structured data and allows for seamless integration with C APIs, making it easier to work with low-level binary data and interoperate with external systems.

Heres what we get if we print the binary data of the structs:

my $type = Struct(q~
    struct {
    char_t title[50];
    char_t author[50];
    char_t subject[100];
    uint32_t book_id;
    } Books;
~);

/* Declare books and do specification */
my $Book1 = StructInst($type, "Sputnik Programming", "Moo Moo", "Telecom Programming Tutorial", UniqueId() % @UInt32Max);
my $Book2 = StructInst($type, "Netscape Failure", "CatDog", "Netscape Failure in a NutShell", UniqueId() % @UInt32Max);

/* Print the binary data of the structs */
say "Binary of Book 1";
say $Book1[<>];

say;

say "Binary of Book 2";
say $Book2[<>];

// PRINTS
//
// Binary of Book 1
// 53 70 75 74 6E 69 6B 20 50 72 6F 67 72 61 6D 6D Sputnik Programm
// 69 6E 67 00 00 00 00 00 00 00 00 00 00 00 00 00 ing.............
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 4D 6F 6F 20 4D 6F 6F 00 00 00 00 00 00 00 ..Moo Moo.......
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 54 65 6C 65 63 6F 6D 20 50 72 6F 67 ....Telecom Prog
// 72 61 6D 6D 69 6E 67 20 54 75 74 6F 72 69 61 6C ramming Tutorial
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 1F 62 EB DA -- -- -- -- .........bëÚ
// 
// Binary of Book 2
// 4E 65 74 73 63 61 70 65 20 46 61 69 6C 75 72 65 Netscape Failure
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 43 61 74 44 6F 67 00 00 00 00 00 00 00 00 ..CatDog........
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 4E 65 74 73 63 61 70 65 20 46 61 69 ....Netscape Fai
// 6C 75 72 65 20 69 6E 20 61 20 4E 75 74 53 68 65 lure in a NutShe
// 6C 6C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ll..............
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 9C 9C BE 5E -- -- -- -- ...........^

In the given example, two instances of a struct called "Books" are created using the StructInst() function. Each instance represents a book with fields for title, author, subject, and book_id. The field values are provided as arguments to the StructInst() function.

To examine the binary data of the struct instances, the [<>] operator is used to print the binary representation. Each field of the struct is displayed as a sequence of hexadecimal bytes. The binary data represents the struct and its field values in a compact and serialized form.

The binary structure follows the layout defined in the struct definition. Each field is represented by a specific number of bytes, and the binary data is organized accordingly. The binary representation allows for efficient storage and transportation of the struct instances, enabling seamless integration with low-level binary operations and external systems.

By working with binary data, the Struct system in Sputnik provides a way to handle structured information in a compact and machine-readable format. It allows for interoperability with C data types and structures, making it easier to interface with external APIs and perform low-level binary operations effectively.

In this next example we modify the struct:

my $type = Struct(q~
    struct {
        struct {
            unsigned short english : 1;
            unsigned short paperBack : 1;
            unsigned short old : 1;
        } flags;
        char_t title[50];
        char_t author[50];
        char_t subject[100];
        uint32_t bookId;
    } Books;
~);

my $mBookId = StructMember($type, "bookId");
my $mTitle = StructMember($type, "title");
my $mSubject = StructMember($type, "subject");
my $mAuthor = StructMember($type, "author");
my $mFlagEng = StructMember($type, "flags.english");
my $mFlagPaperBack = StructMember($type, "flags.paperBack");
my $mFlagOld = StructMember($type, "flags.old");

my $book = StructInst($type, 1, 0, 1, "Sputnik Programming", "Moo Moo", "Telecom Programming Tutorial", UniqueId() % @UInt32Max);

say "Orig Book:";
say "Book Id = " . VecEx($book, $mBookId);
say "Book Title = " . VecEx($book, $mTitle);
say "Book Subject = " . VecEx($book, $mSubject);
say "Book Author = " . VecEx($book, $mAuthor);
say "Book Flag (English) = " . VecEx($book, $mFlagEng);
say "Book Flag (PaperBack) = " . VecEx($book, $mFlagPaperBack);
say "Book Flag (Old) = " . VecEx($book, $mFlagOld);

// Update the book
VecEx($book, $mBookId)++;
VecEx($book, $mTitle) = "SPUTNIK Programming!";
VecEx($book, $mAuthor) = "UberFoX";
VecEx($book, $mFlagPaperBack) = 1;
VecEx($book, $mFlagOld) = 0;

say "\nUpdated Book:";
say "Book Id = " . VecEx($book, $mBookId);
say "Book Title = " . VecEx($book, $mTitle);
say "Book Subject = " . VecEx($book, $mSubject);
say "Book Author = " . VecEx($book, $mAuthor);
say "Book Flag (English) = " . VecEx($book, $mFlagEng);
say "Book Flag (PaperBack) = " . VecEx($book, $mFlagPaperBack);
say "Book Flag (Old) = " . VecEx($book, $mFlagOld);

say "\nBinary of the Book";
say $book[<>];

// PRINTS
//
// Orig Book:
// Book Id = 3664358292
// Book Title = Sputnik Programming
// Book Subject = Telecom Programming Tutorial
// Book Author = Moo Moo
// Book Flag (English) = 1
// Book Flag (PaperBack) = 0
// Book Flag (Old) = 1
// 
// Updated Book:
// Book Id = 3664358293
// Book Title = SPUTNIK Programming!
// Book Subject = Telecom Programming Tutorial
// Book Author = UberFoX
// Book Flag (English) = 1
// Book Flag (PaperBack) = 1
// Book Flag (Old) = 0
// 
// Binary of the Book
// 03 00 53 50 55 54 4E 49 4B 20 50 72 6F 67 72 61 ..SPUTNIK Progra
// 6D 6D 69 6E 67 21 00 00 00 00 00 00 00 00 00 00 mming!..........
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 55 62 65 72 46 6F 58 00 00 00 00 00 ....UberFoX.....
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 54 65 6C 65 63 6F 6D 20 50 72 ......Telecom Pr
// 6F 67 72 61 6D 6D 69 6E 67 20 54 75 74 6F 72 69 ogramming Tutori
// 61 6C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 al..............
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
// 00 00 00 00 00 00 00 00 00 00 00 00 95 AB 69 DA .............«iÚ

This example demonstrates the defining and manipulating a structured data type called "Books." The structure includes nested elements such as flags, title, author, subject, and bookId.

Struct members, such as bookId, title, subject, author, and flags (with subfields english, paperBack, and old), are declared using the Struct function. Each member is assigned a unique identifier using StructMember.

An instance of the Books structure, named "book," is then created using StructInst. The initial values for the book's members are set, including a dynamically generated bookId using the UniqueId function.

The original values of the book's members are printed, showcasing the initial state of the structure. Subsequently, the code updates some of the book's attributes using the VecEx function to access and modify individual members.

The modified values of the book's members are then printed, reflecting the changes made during the update process. Finally, the binary representation of the updated book is displayed using array indexing.

This example highlights the ease with which structured data types can be defined, instantiated, and manipulated in Sputnik. It showcases the language's capabilities for working with complex data structures and performing dynamic updates on their members.

Demonstration of using a wrapper for structs for easier get/set data:

class WStruct
{
    my $StructType;
    my $StructData;
    Function __Construct($structType, "..." $data)
    {
        @this->$StructType = $structType;
        @this->$StructData = StructInst($StructType, **$data);
    }
    Function __Get($index)
    {
        return VecEx($StructData, StructMember($StructType, $index));
    }
    Function __Set($index, $value)
    {
        VecEx($StructData, StructMember($StructType, $index)) = $value;
    }
}

my $b = Struct(q~
    struct {
    char title[50];
    char author[50];
    char subject[100];
    int book_id;
    } Books;
~);

/* Declare books and do specification */
my $Book1 = new WStruct($b, "Sputnik Programming", "UberFoX", "Sputnik Programming Tutorial", 123456);
my $Book2 = new WStruct($b, "C++ Programming", "Sharpy", "C++ Programming Tutorial", 654321);

$Book1["book_id"] = 654321;
$Book2["book_id"] = 123456;

/* print Book1 info */
printf("Book 1 title : %s\n", $Book1["title"]);
printf("Book 1 author : %s\n", $Book1["author"]);
printf("Book 1 subject : %s\n", $Book1["subject"]);
printf("Book 1 book_id : %d\n", $Book1["book_id"]);

/* print Book2 info */
printf("Book 2 title : %s\n", $Book2["title"]);
printf("Book 2 author : %s\n", $Book2["author"]);
printf("Book 2 subject : %s\n", $Book2["subject"]);
printf("Book 2 book_id : %d\n", $Book2["book_id"]);

// Book 1 title : Sputnik Programming
// Book 1 author : UberFoX
// Book 1 subject : Sputnik Programming Tutorial
// Book 1 book_id : 654321
// Book 2 title : C++ Programming
// Book 2 author : Sharpy
// Book 2 subject : C++ Programming Tutorial
// Book 2 book_id : 123456

This example demonstrates the creation of two book instances using the WStruct wrapper. It allows easy access to struct members using square bracket notation for both setting and getting values.

Normally we use [<>] (Print Array) when showing the content of structs sometimes BinaryExpand however in this example we created a printr_r to do the printing which works exactly the same as BinaryExpand but done in a way we can see it in action:

Function print_r($data)
{
    my $length = length($data);
    for(my $i = 0; $i < $length; $i++)
    {
        if ($i > 0 && $i % 16 == 0)
            printf("\n");
        printf("%02X ", $data[$i]);
        if (($i + 1) % 16 == 0 || $i + 1 == $length)
        {
            while (($i + 1) % 16 != 0)
            {
                printf("   ");
                $i++;
            }
            printf(" ");
            for(my $j = $i - 15; $j <= $i; $j++)
            {
                if ($j < $length)
                {
                    if(OfPrintable((Char)$data[$j]))
                        printf("%c", (Char)$data[$j]);
                    else
                        printf(".");
                }
            }
        }
    }
    printf("\n");
}

my $type = Struct(q~
    struct {
        struct {
            unsigned short english : 1;
            unsigned short paperBack : 1;
            unsigned short old : 1;
        } flags;
        char_t title[50];
        char_t author[50];
        char_t subject[100];
        uint32_t bookId;
    } Books;
~);

my $book = StructInst($type, 1, 0, 1, "Sputnik Programming", "Moo Moo", "Telecom Programming Tutorial", UniqueId() % @UInt32Max);

print_r($book);

// PRINTS
// 05 00 53 70 75 74 6E 69 6B 20 50 72 6F 67 72 61  ..Sputnik Progra
// 6D 6D 69 6E 67 00 00 00 00 00 00 00 00 00 00 00  mming...........
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
// 00 00 00 00 4D 6F 6F 20 4D 6F 6F 00 00 00 00 00  ....Moo Moo.....
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
// 00 00 00 00 00 00 54 65 6C 65 63 6F 6D 20 50 72  ......Telecom Pr
// 6F 67 72 61 6D 6D 69 6E 67 20 54 75 74 6F 72 69  ogramming Tutori
// 61 6C 00 00 00 00 00 00 00 00 00 00 00 00 00 00  al..............
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
// 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
// 00 00 00 00 00 00 00 00 00 00 00 00 E6 27 BC 75  .............'.u

In the next example we directly create the structs instead of their type:

my $p1 = Struct(q~
    struct {
    int x;
    int y;
    char[12] name;
    } NamedPoint;
~, 10, 20, "cat");

my $p2 = Struct(q~
    struct {
    int x;
    int y;
    char[12] name;
    } NamedPoint = {40, 60, "dog"};
~);

say $p1[<>];
say $p2[<>];

// PRINTS
// 0A 00 00 00 14 00 00 00 63 61 74 00 00 00 00 00 ........cat.....
// 00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- ....
// 28 00 00 00 3C 00 00 00 64 6F 67 00 00 00 00 00 (...<...dog.....
// 00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- ....

In the given code, two instances of the NamedPoint struct are created using an alternative way of using Struct(). The first instance, $p1, is initialized with values of x = 10, y = 20, and name = "cat". The second instance, $p2, uses the syntax {40, 60, "dog"} to directly initialize the struct members.

When printing the binary representation of $p1 and $p2, we can see the corresponding values. However, since these structs were created using the alternative method, the type information is not available.

This approach can be useful in certain scenarios where the type information is not necessary or when you have the struct layout defined elsewhere. However, if you need to access or modify the struct members dynamically, it is recommended to use the regular Struct() method, which provides type information for proper manipulation.

Additionally, this type of struct creation without explicit type information is commonly used in network socket programming. It allows for the convenient creation of "throw away" structs that can be easily sent as packets to a server without the need to define explicit types or worry about complex type definitions.

Using this approach, developers can quickly assemble the necessary data into a binary representation that can be transmitted over the network. It offers a streamlined way to serialize and deserialize data without the overhead of managing complex type definitions.

However, it's important to note that this method sacrifices some of the type safety and clarity provided by explicit type definitions. Care must be taken to ensure that the sender and receiver agree on the struct layout and interpret the received data correctly.

Here we do same as above but predefine the struct type:

StructDef(q~
    struct NamedPoint {
    int x;
    int y;
    char[12] name;
    } NamedPoint;
~);


my $p1 = Struct(q~
    struct NamedPoint tmp;
~, 10, 20, "cat");

my $p2 = Struct(q~
    struct NamedPoint tmp = {40, 60, "dog"};
~);

say $p1[<>];
say $p2[<>];

// PRINTS
// 0A 00 00 00 14 00 00 00 63 61 74 00 00 00 00 00 ........cat.....
// 00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- ....
// 28 00 00 00 3C 00 00 00 64 6F 67 00 00 00 00 00 (...<...dog.....
// 00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- ....

The only difference in this example is how the structs are created. Instead of using the Struct() function directly, we first define the struct using StructDef(), and then create the struct instances using the defined struct name.

Same as above but a more simple one line style Struct():

StructDef(q~
    struct NamedPoint {
    int x;
    int y;
    char[12] name;
    } NamedPoint;
~);


my $p1 = Struct(q(struct NamedPoint tmp), 10, 20, "cat");
my $p2 = Struct(q(struct NamedPoint tmp = {40, 60, "dog"}));

say $p1[<>];
say $p2[<>];

// PRINTS
// 0A 00 00 00 14 00 00 00 63 61 74 00 00 00 00 00 ........cat.....
// 00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- ....
// 28 00 00 00 3C 00 00 00 64 6F 67 00 00 00 00 00 (...<...dog.....
// 00 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- ....

The only difference in this example is that the struct definitions are written as one-liners without the semicolon. Other than that, the struct instances are created and accessed in the same way as before.

The Struct() function in Sputnik is not just limited to creating traditional structs. It provides a powerful and versatile way to define and work with various binary structures, including scalars, strings, and arrays. It allows you to efficiently handle different types of data in a structured manner. Here's an example that demonstrates how Struct() can be used to create and access different types of data:

my $p1 = Struct(q(char[] tmp = "Hello World!"));
my $p2 = Struct(q(int[] tmp = {1, 2, 3, 4, 5}));
my $p3 = Struct(q(double tmp = 777.42));

say $p1[<>];
say $p2[<>];
say $p3[<>];

// PRINTS
// 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 00 -- -- -- Hello World!.
// 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................
// 05 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- ....
// 8F C2 F5 28 5C 4B 88 40 -- -- -- -- -- -- -- -- .Âõ(\K.@

In this example, we create different binary structures using the Struct() function. We define a character array, an integer array, and a double precision floating-point number. The Struct() function allows us to easily initialize and access these structures. This highlights the flexibility and power of the Struct() function in handling diverse types of binary data structures.

You can also read/write to this type like so:

my list("@Type" $ty, "@Data" $data) = StructAssoc(q(int[] tmp = {1, 2, 3, 4, 5}))["tmp"];
say VecEx($data, $ty, 0);
say VecEx($data, $ty, 1);
say VecEx($data, $ty, 2);
say VecEx($data, $ty, 3);
say VecEx($data, $ty, 4);
VecEx($data, $ty, 0) += 10;
VecEx($data, $ty, 1) += 10;
VecEx($data, $ty, 2) += 10;
VecEx($data, $ty, 3) += 10;
VecEx($data, $ty, 4) += 10;
say VecEx($data, $ty, 0);
say VecEx($data, $ty, 1);
say VecEx($data, $ty, 2);
say VecEx($data, $ty, 3);
say VecEx($data, $ty, 4);
// PRINTS
// 1
// 2
// 3
// 4
// 5
// 11
// 12
// 13
// 14
// 15

When VexEx is given a type + index it will read/write the elements at them indexes using the type as a guide.

There is another way to the array using the TypeNewArrayOf is like this:

my list($ty, $data) = TypeNewArrayOf(@TyInt, 3, true, [500, 600, 700]);
say VecEx($data, $ty, 0);
say VecEx($data, $ty, 1);
say VecEx($data, $ty, 2);
// PRINTS
// 500
// 600
// 700
The above can also be done with multiple dimensions like so:
my list("@Type" $ty, "@Data" $data) = StructAssoc(q(int[3][2] tmp = {{1, 2}, {3, 4}, {5, 6}}))["tmp"];
printr VecEx($data, $ty, 0);
VecEx($data, $ty, 0) = [100, 200];
printr VecEx($data, $ty, 0);
printr VecEx($data, $ty, 1);
printr VecEx($data, $ty, 2);
// PRINTS
// Array
// (
//     [0] => 1
//     [1] => 2
// )
// Array
// (
//     [0] => 100
//     [1] => 200
// )
// Array
// (
//     [0] => 3
//     [1] => 4
// )
// Array
// (
//     [0] => 5
//     [1] => 6
// )

Wat happened here is we created a new 3 element array of @TyInt and initalized it with some values. It will accept any type however it is best to use the default types provided by the macros.

There is another way to the array using the TypeNewArrayOf is like this:

my $tyIntArray = TypeNewArrayOf(@TyInt, 2);
my list($ty, $data) = TypeNewArrayOf($tyIntArray, 3, true, [[1, 2], [3, 4], [5, 6]]);
printr VecEx($data, $ty, 0);
printr VecEx($data, $ty, 1);
printr VecEx($data, $ty, 2);
// PRINTS
// Array
// (
//     [0] => 1
//     [1] => 2
// )
// Array
// (
//     [0] => 3
//     [1] => 4
// )
// Array
// (
//     [0] => 5
//     [1] => 6
// )

To understand how the above works see this:

my $ty = TypeNewArrayOf(@TyInt, 3);
printr TypeDefInfo($ty);
// PRINTS
// Array
// (
//     [Kind] => 14
//     [KindStr] => Array
//     [Size] => 12
//     [Align] => 4
//     [IsUnsigned] => false
//     [IsAtomic] => false
//     [Base] => Array
//         (
//             [Kind] => 5
//             [KindStr] => Int
//             [Size] => 4
//             [Align] => 4
//             [IsUnsigned] => false
//             [IsAtomic] => false
//             [Name] => N/A
//             [ArrayLen] => 0
//             [ArraySpk] => false
//             [IsFlexible] => false
//             [IsPacked] => false
//             [IsNaked] => false
//             [BinF] => false
//             [ListF] => false
//             [IsVariadic] => false
//         )
//     [Name] => N/A
//     [ArrayLen] => 3
//     [ArraySpk] => false
//     [IsFlexible] => false
//     [IsPacked] => false
//     [IsNaked] => false
//     [BinF] => false
//     [ListF] => false
//     [IsVariadic] => false
// )

So what it is doing is creating a new type which defines all the information we need to create/read/write this type.

Here is an example of StructAssoc() which allows returning the struct type and data at the same time:

my list("@Type" $ty, "@Data" $packet) = StructAssoc(q~
    struct {
        uint16_t packetType;
        uint32_t userID;
        uint8_t action;
        uint8_t inventorySlot;
        uint8_t mouseButton;
        uint8_t modifiers;
    } packet = {
        0x1234,          // packet type identifier
        123456789,       // user ID
        0x01,            // action for accessing inventory
        56,              // inventory slot number
        0x01,            // mouse button (left click)
        0x02             // modifier keys (shift key)
    };
~)["packet"];

printf("Packet Type: 0x%X\n", VecEx($packet, StructMember($ty, "packetType")));
printf("User ID: 0x%X\n", VecEx($packet, StructMember($ty, "userID")));
printf("Action: 0x%X\n", VecEx($packet, StructMember($ty, "action")));
printf("Inventory Slot: 0x%X\n", VecEx($packet, StructMember($ty, "inventorySlot")));
printf("Mouse Button: 0x%X\n", VecEx($packet, StructMember($ty, "mouseButton")));
printf("Modifiers: 0x%X\n", VecEx($packet, StructMember($ty, "modifiers")));

say $packet[<>];

// PRINTS
// Packet Type: 0x1234
// User ID: 0x75BCD15
// Action: 0x1
// Inventory Slot: 0x38
// Mouse Button: 0x1
// Modifiers: 0x2
// 34 12 00 00 15 CD 5B 07 01 38 01 02 -- -- -- -- 4....Í[..8..

The code you provided demonstrates the usage of StructAssoc to create a structure and retrieve its type and data. The packet variable is initialized with the specified values, and then the hexadecimal values of each member are printed using printf with the %X format specifier.

The output shows the hexadecimal values of each member of the packet structure, as well as the binary representation of the packet itself.

It's indeed a convenient way to create and initialize structures in a formatted manner.


Struct Definition

Structs in Sputnik allow you to define custom data types with specific fields. They are defined using C syntax, specifying the field names and their corresponding types. The fields can be simple types such as bool_t, char_t, int8_t, int16_t, int32_t, or other data types.

Structs can also be nested within other structs, creating a hierarchical structure. This allows for organizing related data within a single struct definition.

By using structs, you can create structured data objects that contain multiple fields, each with its own type. This provides a convenient way to organize and manipulate data in Sputnik, especially when working with low-level binary operations or interacting with external systems.

For more specific examples and visual representation, refer to the provided examples and code snippets in the previous sections.

When defining a struct using the Struct function in Sputnik, it is important to note the following:

Naming the Struct: The name provided after the closing brace (}) in the struct definition is critical and must exist. In the example given, the struct is named "Point". The name allows you to refer to the struct type when creating struct instances or accessing its members. If the name is omitted, the struct will not compile successfully.

my $type = Struct(q~
    struct {
        int x;
        int y;
    } Point;  // required
~);

Ignoring Struct Name: Although you can include a name for the struct, such as "Testy" in the example below, it will be ignored. This means that the name serves no functional purpose and is not used for referencing the struct or its instances. It is best to omit the name if it is not necessary for your code's clarity or understanding.

my $type = Struct(q~
    struct Testy { // wrong
        int x;
        int y;
    } Point;
~);

When defining a struct using the Struct function in Sputnik, ensure that the struct has a name after the closing brace. The name is critical for referencing the struct type.

It is important to note that structs in Sputnik are standalone entities and cannot directly reference other structs unless they are included within the same struct definition (or a StructDef() but we will get to that in a minute). In such cases, the included structs must have names.

For example, consider the following code snippet:

my $typeBounds = Struct(q~
    struct Size {
        int width;
        int height;
    };
    struct Point {
        int x;
        int y;
    };
    struct {
        struct Size sz;
        struct Point pt;
    } Bounds;
~);

my $p = StructInst($typeBounds, [10, 20], [20, 30]);

say $p[<>];
printr FromStruct($typeBounds, $p);

// PRINTS
// 0A 00 00 00 14 00 00 00 14 00 00 00 1E 00 00 00 ................
// Array
// (
//     [sz] => Array
//         (
//             [width] => 10
//             [height] => 20
//         )
//     [pt] => Array
//         (
//             [x] => 20
//             [y] => 30
//         )
// )

In this example, the Bounds struct includes two other structs, Size and Point, which are defined within the same struct definition. The structs Size and Point must have names because they are referenced in the Bounds struct. This allows us to create struct instances like $p = StructInst($typeBounds, [10, 20], [20, 30]), where we provide values for the sz and pt members.

If the Size and Point structs were defined separately outside the Bounds struct, they would not be directly accessible or usable within the Bounds struct definition. To include them in the Bounds struct, they would need to be defined within the struct definition, and proper names should be given to those included structs.

To use other structs within a struct definition in Sputnik, include them in the same struct definition and provide names for those included structs. This allows for the creation of struct instances with nested structures, providing a convenient way to organize and access complex data structures.

Alternatively you can predefine the struct definitions using the StructDef() function and use them later for example:

StructDef(q~
    struct Size {
        int width;
        int height;
    };
~);

StructDef(q~
    struct Point {
        int x;
        int y;
    };
~);

my $typeBounds = Struct(q~
    struct {
        struct Size sz;
        struct Point pt;
    } Bounds;
~);

my $p = StructInst($typeBounds, [10, 20], [20, 30]);

say $p[<>];
printr FromStruct($typeBounds, $p);

// PRINTS
// 0A 00 00 00 14 00 00 00 14 00 00 00 1E 00 00 00 ................
// Array
// (
//     [sz] => Array
//         (
//             [width] => 10
//             [height] => 20
//         )
//     [pt] => Array
//         (
//             [x] => 20
//             [y] => 30
//         )
// )

By using the StructDef function, you can create a definition of a struct that can be used later inside other structs to create nested structs. This is a powerful feature that allows you to define struct types separately and reuse them as needed.

In the example above, we first define the Size struct and the Point struct using StructDef. Then, we define the Bounds struct, which contains nested structs sz of type Size and pt of type Point. We can then create an instance of the Bounds struct ($p) and access its members using array syntax.

This approach allows for modular struct definitions, making your code more organized and easier to maintain. You can define the necessary structs separately and combine them as needed to create complex structures.

Same as above but placing multiple struct definitions into a single StructDef:

StructDef(q~
    struct Size {
        int width;
        int height;
    };
    struct Point {
        int x;
        int y;
    };
~);

my $typeBounds = Struct(q~
    struct {
        struct Size sz;
        struct Point pt;
    } Bounds;
~);

my $p = StructInst($typeBounds, [10, 20], [20, 30]);

say $p[<>];
printr FromStruct($typeBounds, $p);

// PRINTS
// 0A 00 00 00 14 00 00 00 14 00 00 00 1E 00 00 00 ................
// Array
// (
//     [sz] => Array
//         (
//             [width] => 10
//             [height] => 20
//         )
//     [pt] => Array
//         (
//             [x] => 20
//             [y] => 30
//         )
// )

Here we create 3 structs at once in a single Struct() call:

my $st = StructDef(q~
    struct BFLDTESTR {
        byte a;
        int b : 5;
        int c : 10;
    };
~);

my list($g1, $g2, $g3) = Struct(q~
    struct BFLDTESTR v1 = {1, 2, 3}, v2 = {4, 5, 6}, v3={};
~);

say VecEx($g1, StructMember($st, "a"));
say VecEx($g1, StructMember($st, "b"));
say VecEx($g1, StructMember($st, "c"));
say VecEx($g2, StructMember($st, "a"));
say VecEx($g2, StructMember($st, "b"));
say VecEx($g2, StructMember($st, "c"));
say VecEx($g3, StructMember($st, "a"));
say VecEx($g3, StructMember($st, "b"));
say VecEx($g3, StructMember($st, "c"));

// PRINTS
// 1
// 2
// 3
// 4
// 5
// 6
// 0
// 0
// 0

The code defines a struct type named "BFLDTESTR" with three fields. It creates three struct instances: v1, v2, and v3. Using the Struct() function, the instances are assigned to variables $g1, $g2, and $g3 in the order they were defined. The VecEx() function is used to access the values of the struct members. Finally, the values are printed for each struct instance.

Same as above but this time using StructAssoc() function so it will always return

my $st = StructDef(q~
    struct BFLDTESTR {
        byte a;
        int b : 5;
        int c : 10;
    };
~);

my list("fox" $g1, "cat" $g2, "dog" $g3) = StructAssoc(q~
    struct BFLDTESTR cat = {4, 5, 6}, dog ={}, fox = {1, 2, 3};
~);

say VecEx($g1, StructMember($st, "a"));
say VecEx($g1, StructMember($st, "b"));
say VecEx($g1, StructMember($st, "c"));
say VecEx($g2, StructMember($st, "a"));
say VecEx($g2, StructMember($st, "b"));
say VecEx($g2, StructMember($st, "c"));
say VecEx($g3, StructMember($st, "a"));
say VecEx($g3, StructMember($st, "b"));
say VecEx($g3, StructMember($st, "c"));

// PRINTS
// 1
// 2
// 3
// 4
// 5
// 6
// 0
// 0
// 0

This code uses StructAssoc() to create struct instances without prior definition and retrieve both the binary data and type. It allows easy access to struct members using associative array syntax. Here's a concise summary:

Using StructAssoc(), you can create and access struct instances without predefined definitions. It returns both the binary data and type as associative arrays. By assigning the struct instances to variables ($g1, $g2, $g3), you can access their data and type using associative array syntax. StructMember() retrieves specific member values, and VecEx() extracts those values from the struct data.

We can use StructAssoc() to produce a ton of data and types as shown here:

printr StructAssoc(q~
    struct {
        byte a;
        int b : 5;
        int c : 10;
    } cat = {4, 5, 6}, dog ={};
    struct {
        int x;
        int y;
    } pointa = {100, 200}, pointb = {400, 600};
    int[] kitty = {100, 200, 300};
    char[] testy = "Hello world!";
    double t = 100;
    int y = 600;
~);

// PRINTS
// Array
// (
//     [y] => Array
//         (
//             [Type] => {TypeDef=y,Size=0,Align=4,IsUnsigned=4}
//             [Data] => Binary
//                 (
//                     [0] => 0x58 [88] (X)
//                     [1] => 0x02 [2] (.)
//                     [2] => 0x00 [0] (.)
//                     [3] => 0x00 [0] (.)
//                 )
//         )
//     [t] => Array
//         (
//             [Type] => {TypeDef=t,Size=0,Align=8,IsUnsigned=8}
//             [Data] => Binary
//                 (
//                     [0] => 0x00 [0] (.)
//                     [1] => 0x00 [0] (.)
//                     [2] => 0x00 [0] (.)
//                     [3] => 0x00 [0] (.)
//                     [4] => 0x00 [0] (.)
//                     [5] => 0x00 [0] (.)
//                     [6] => 0x59 [89] (Y)
//                     [7] => 0x40 [64] (@)
//                 )
//         )
//     [testy] => Array
//         (
//             [Type] => {TypeDef=N/A,Size=0,Align=13,IsUnsigned=1}
//             [Data] => Binary
//                 (
//                     [0] => 0x48 [72] (H)
//                     [1] => 0x65 [101] (e)
//                     [2] => 0x6C [108] (l)
//                     [3] => 0x6C [108] (l)
//                     [4] => 0x6F [111] (o)
//                     [5] => 0x20 [32] ( )
//                     [6] => 0x77 [119] (w)
//                     [7] => 0x6F [111] (o)
//                     [8] => 0x72 [114] (r)
//                     [9] => 0x6C [108] (l)
//                     [10] => 0x64 [100] (d)
//                     [11] => 0x21 [33] (!)
//                     [12] => 0x00 [0] (.)
//                 )
//         )
//     [kitty] => Array
//         (
//             [Type] => {TypeDef=N/A,Size=0,Align=12,IsUnsigned=4}
//             [Data] => Binary
//                 (
//                     [0] => 0x64 [100] (d)
//                     [1] => 0x00 [0] (.)
//                     [2] => 0x00 [0] (.)
//                     [3] => 0x00 [0] (.)
//                     [4] => 0xC8 [200] (È)
//                     [5] => 0x00 [0] (.)
//                     [6] => 0x00 [0] (.)
//                     [7] => 0x00 [0] (.)
//                     [8] => 0x2C [44] (,)
//                     [9] => 0x01 [1] (.)
//                     [10] => 0x00 [0] (.)
//                     [11] => 0x00 [0] (.)
//                 )
//         )
//     [pointb] => Array
//         (
//             [Type] => {TypeDef=pointb,Size=0,Align=8,IsUnsigned=4}
//             [Data] => Binary
//                 (
//                     [0] => 0x90 [144] (.)
//                     [1] => 0x01 [1] (.)
//                     [2] => 0x00 [0] (.)
//                     [3] => 0x00 [0] (.)
//                     [4] => 0x58 [88] (X)
//                     [5] => 0x02 [2] (.)
//                     [6] => 0x00 [0] (.)
//                     [7] => 0x00 [0] (.)
//                 )
//         )
//     [pointa] => Array
//         (
//             [Type] => {TypeDef=pointb,Size=0,Align=8,IsUnsigned=4}
//             [Data] => Binary
//                 (
//                     [0] => 0x64 [100] (d)
//                     [1] => 0x00 [0] (.)
//                     [2] => 0x00 [0] (.)
//                     [3] => 0x00 [0] (.)
//                     [4] => 0xC8 [200] (È)
//                     [5] => 0x00 [0] (.)
//                     [6] => 0x00 [0] (.)
//                     [7] => 0x00 [0] (.)
//                 )
//         )
//     [dog] => Array
//         (
//             [Type] => {TypeDef=dog,Size=0,Align=4,IsUnsigned=4}
//             [Data] => Binary
//                 (
//                     [0] => 0x00 [0] (.)
//                     [1] => 0x00 [0] (.)
//                     [2] => 0x00 [0] (.)
//                     [3] => 0x00 [0] (.)
//                 )
//         )
//     [cat] => Array
//         (
//             [Type] => {TypeDef=dog,Size=0,Align=4,IsUnsigned=4}
//             [Data] => Binary
//                 (
//                     [0] => 0x04 [4] (.)
//                     [1] => 0xC5 [197] (Å)
//                     [2] => 0x00 [0] (.)
//                     [3] => 0x00 [0] (.)
//                 )
//         )
// )

The StructAssoc() function is a powerful tool that allows you to mass produce different types of data structures in Sputnik. It enables you to create structs, variables, strings, and any other kind of data needed for various purposes, such as APIs or network sockets.

One of the key advantages of StructAssoc() is that it not only returns the binary data of the created structures but also provides the type information associated with each item. This type information can be utilized to easily read from and write to the data.

In the example provided, StructAssoc() is used to define and create multiple structs (cat, dog), variables (t, y), an array (kitty), and a string (testy). The returned value of StructAssoc() is then printed, displaying the binary data and associated type for each item.

The StructAssoc() function simplifies the process of creating and managing different types of data structures by providing both the data and the corresponding type information. This makes it easier to work with the data, ensuring efficient handling and manipulation within your code.

Here we create an instance of a struct that was defined using StructDef():

my $tyPonit = StructDef(q~
    struct PACKED Point {
        int x;
        int y;
    };
~);

my $p = StructInst($tyPonit, 100, 200);

say "X=" . VecEx($p, StructMember($tyPonit, "x"));
say "Y=" . VecEx($p, StructMember($tyPonit, "y"));

VecEx($p, StructMember($tyPonit, "x")) += 10;
VecEx($p, StructMember($tyPonit, "y")) += 77;

say "X=" . VecEx($p, StructMember($tyPonit, "x"));
say "Y=" . VecEx($p, StructMember($tyPonit, "y"));

say $p[<>];

// X=100
// Y=200
// X=110
// Y=277
// 64 00 00 00 C8 00 00 00 -- -- -- -- -- -- -- -- d...È...

In this example, the StructDef function is used to define the structure of the Point struct. This allows the Point struct to be nested in other structures if needed. The StructInst function is then used to create an instance of the Point struct, initialized with values for the "x" and "y" members.

The rest of the code follows a similar pattern as before. The values of "x" and "y" are printed, modified, and then printed again to show the changes. Finally, the binary representation of the struct instance is printed using "$p[<>]".

Overall, the example demonstrates the usage of StructDef and StructInst to define and create struct instances in Sputnik. It highlights the flexibility of the language in working with structs, whether they are standalone or nested within other structures.

When using the StructDef function in Sputnik to define a struct, it's important to understand the naming convention for the struct itself. In the original usage, the name provided is actually the name of the variable or table key where the struct will be stored. The struct itself doesn't have an explicit name.

However, to give the struct a proper name, it is recommended to follow the correct syntax. Here's an example:

my $tyPoint = StructDef(q~
    struct PACKED Point {
        int x;
        int y;
    } Point;
~);

In this revised approach, the struct is defined using StructDef, and the name "Point" is provided at the end of the struct definition. This assigns a distinct name to the struct itself, in addition to storing it in the type table with the same name. This naming convention helps ensure clarity and consistency when referring to the struct in code and when using functions like StructInfo.

Therefore, it is highly recommended to always include the struct's true name at the bottom of the StructDef, unless it is an anonymous struct. This approach not only improves performance but also ensures consistency and maintainability in your code.

By following this naming convention, you can create structured types with proper names in Sputnik, enhancing the readability and maintainability of your code.

Please note that the struct's name provided at the end of the StructDef call should match the name used to reference the struct when creating instances or accessing its members.

Here we get a print out of a struct definition:

my $type = Struct(q~
    struct {
        int x;
        int y;
    } Point;
~);

printr StructInfo($type);

// PRINTS
// Array
// (
//     [Kind] => Struct
//     [Size] => 8
//     [Align] => 4
//     [IsUnsigned] => False
//     [IsAtomic] => False
//     [Name] => Point
//     [ArrayLen] => 0
//     [ArraySpk] => False
//     [IsFlexible] => False
//     [IsPacked] => False
//     [IsVariadic] => False
//     [Members] => Array
//         (
//             [0] => Array
//                 (
//                     [Name] => x
//                     [Type] => Array
//                         (
//                             [Kind] => Int
//                             [Size] => 4
//                             [Align] => 4
//                             [IsUnsigned] => False
//                             [IsAtomic] => False
//                             [Name] => x
//                             [ArrayLen] => 0
//                             [ArraySpk] => False
//                             [IsFlexible] => False
//                             [IsPacked] => False
//                             [IsVariadic] => False
//                         )
//                     [Index] => 0
//                     [Offset] => 0
//                     [IsBitfield] => False
//                     [BitOffset] => 0
//                     [BitWidth] => 0
//                 )
//             [1] => Array
//                 (
//                     [Name] => y
//                     [Type] => Array
//                         (
//                             [Kind] => Int
//                             [Size] => 4
//                             [Align] => 4
//                             [IsUnsigned] => False
//                             [IsAtomic] => False
//                             [Name] => y
//                             [ArrayLen] => 0
//                             [ArraySpk] => False
//                             [IsFlexible] => False
//                             [IsPacked] => False
//                             [IsVariadic] => False
//                         )
//                     [Index] => 1
//                     [Offset] => 4
//                     [IsBitfield] => False
//                     [BitOffset] => 0
//                     [BitWidth] => 0
//                 )
//         )
// )

The StructInfo function in Sputnik provides detailed information about a struct's layout and members. It is a powerful tool for understanding the structure and composition of a given struct. Let's break down the output of the StructInfo function for the provided example:

The output provides an array of information about the struct:

The output also includes an array of members within the struct:

The StructInfo function is valuable as it allows you to inspect and understand the structure of a given struct, including its size, alignment, and member details. With this information, you can manually read and manipulate the struct's binary data without relying solely on automated tools. It provides a higher level of control and understanding when working with structs in Sputnik.

The StructInst function is used to create an instance of a struct defined by a struct type. It allows you to populate the struct's fields with specific values. The function takes the struct type as the first argument, followed by the values to be assigned to each field.

In the case of nested structs, such as in your example with $typeBounds, each nesting level requires an array to represent the values. This is because each nesting level corresponds to a separate field within the struct.

For example, in your $typeBounds struct, you have two nested structs: Size and Point. Each of these nested structs has its own fields (width and height for Size, and x and y for Point). To create an instance of the Bounds struct, you would use the StructInst function and provide values for each nested struct's fields.

Using your example:

StructInst($typeBounds, [10, 20], [20, 30]);

Here, [10, 20] represents the values for the Size struct, and [20, 30] represents the values for the Point struct. The arrays are used to maintain the structure and order of the nested fields.

In the case of an array field within a struct, such as int[], you would use an array to represent the values of the array field. For example:

my $typeArray = Struct(q~
    struct {
        int[] numbers;
    } ArrayStruct;
~);

To create an instance of ArrayStruct with values for the numbers field, you would provide an array of values:

StructInst($typeArray, [1, 2, 3, 4, 5]);

In this case, [1, 2, 3, 4, 5] represents the values for the numbers field, maintaining the array structure within the struct.

This shows how it looks:

It's important to note that the nesting and array structures in the StructInst function should align with the defined struct type to ensure correct assignment of values to the corresponding fields.


Unpacking a Struct

Once you have a struct instance represented as binary data, you may need to unpack it back into a Sputnik array or access its individual values. There are multiple approaches to achieve this, depending on your requirements.

One way to unpack a struct is by using the FromStruct function. This function takes the struct type definition and the binary data of the struct, and returns a Sputnik array representing the unpacked struct. Here's an example:

my $structData = B"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00";
my $type = Struct(q~
    struct {
        int x;
        int y;
        int z;
    } MyStruct;
~);

my $unpackedArray = FromStruct($type, $structData);
printr $unpackedArray;

// PRINTS
// Array
// (
//     [x] => 1
//     [y] => 2
//     [z] => 3
// )

In the code snippet above, FromStruct is used to unpack the struct represented by $structData into the $unpackedArray variable. The resulting array will have keys corresponding to the field names defined in the struct, with their corresponding values.

Alternatively, you can use the VecEx function/operator along with the StructMember function to access the individual values of a struct without unpacking it into a Sputnik array. Here's an example:

my $structData = "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00";
my $type = Struct(q~
    struct {
        int x;
        int y;
        int z;
    } MyStruct;
~);

my $xValue = VecEx($structData, StructMember($type, "x"));
my $yValue = VecEx($structData, StructMember($type, "y"));
my $zValue = VecEx($structData, StructMember($type, "z"));

say $xValue;
say $yValue;
say $zValue;

// PRINTS
// 1
// 2
// 3

In the code snippet above, VecEx is used with StructMember to directly access the values of individual fields in the struct represented by $structData. The $xValue, $yValue, and $zValue variables will hold the respective values extracted from the struct.

Both approaches provide flexibility in accessing and unpacking the binary data of a struct. Choose the method that best suits your needs based on whether you prefer working with the entire struct as an array or accessing individual field values directly.

Additionally, when using the FromStruct function or VecEx with StructMember to unpack a struct, it's important to note that the size of array-type fields must be explicitly specified as absolute numbers. For example, if a struct has an array field of size unknown or variable length, it cannot be directly unpacked.

To work around this limitation, you have a couple of options. One approach is to define a struct that specifically defines the size of the array field, allowing you to unpack the binary data back into an array. This requires knowing the exact size of the array beforehand.

Another approach is to create a new typedef that contains the desired type you want to read from the struct, but set the argument false to avoid saving the type to the type table. This allows you to use the typedef once without permanently saving it. By doing so, you can use FromTypeDef along with the typedef to unpack the struct into a Sputnik array.

It's important to ensure that you have the necessary information about the struct's layout, including the sizes of array-type fields, in order to successfully unpack the binary data into a usable format.

my $typeArray = Struct(q~
    struct {
        int[] numbers;
    } ArrayStruct;
~);

my $v = StructInst($typeArray, [1, 2, 3, 4, 5]);

printr $v[<>];
printr FromTypeDef(Struct(q(struct { int[5] numbers; } ArrayStruct;)), $v);
printr FromTypeDef(TypeDef(q(int[5] numbers), false), $v);

// PRINTS
// 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................
// 05 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- ....
// Array
// (
//     [numbers] => Array
//         (
//             [0] => 1
//             [1] => 2
//             [2] => 3
//             [3] => 4
//             [4] => 5
//         )
// )
// Array
// (
//     [0] => 1
//     [1] => 2
//     [2] => 3
//     [3] => 4
//     [4] => 5
// )

In the given example, the struct ArrayStruct has an array field named numbers of size 5. Using the StructInst function, an instance of the struct is created with values [1, 2, 3, 4, 5]. To unpack the struct and access the array field, the FromTypeDef function is used with a matching typedef that defines the array size. The resulting unpacked array contains the values [1, 2, 3, 4, 5].


Additionally, the example demonstrates two options for unpacking the ArrayStruct with the array field numbers. The first method uses the FromTypeDef function with a matching typedef, specifying the array size as [5]. This approach provides explicit control over the array size during unpacking, resulting in an array with values [1, 2, 3, 4, 5].

The second method employs the FromStruct function, which automatically unpacks the struct back into a Sputnik array. This approach infers the array size from the original struct definition and requires no additional specifications. The resulting unpacked array also contains the values [1, 2, 3, 4, 5].

These options offer flexibility when working with structs, allowing you to choose the method that best fits your requirements and coding style.


Accessing Struct Members

To read from or write to struct members, you use the VecEx() function/operator. It allows you to access and modify specific members of a struct using a member locator obtained from the StructMember() function. The VecEx() function/operator provides a way to interact with the binary data in the struct.

In the provided code sample, we demonstrate how to access and modify the members of a struct using the VecEx() function/operator in Sputnik.

my $type = Struct(q~
    struct {
        int x;
        int y;
        int z;
        char[16] name;
    } MyStruct;
~);

my $struct = StructInst($type, 100, 200, 300, "Hello World");

say VecEx($struct, StructMember($type, "x"));
say VecEx($struct, StructMember($type, "y"));
say VecEx($struct, StructMember($type, "z"));
say VecEx($struct, StructMember($type, "name"));

VecEx($struct, StructMember($type, "x")) += 20;
VecEx($struct, StructMember($type, "y")) /= 2;
VecEx($struct, StructMember($type, "z")) >>= 4;
VecEx($struct, StructMember($type, "name")) = "Hello Kitty!";

say VecEx($struct, StructMember($type, "x"));
say VecEx($struct, StructMember($type, "y"));
say VecEx($struct, StructMember($type, "z"));
say VecEx($struct, StructMember($type, "name"));

// PRINTS
// 100
// 200
// 300
// Hello World
// 120
// 100
// 18
// Hello Kitty!

First, we define a struct type named "MyStruct" with four members: "x" (an int), "y" (an int), "z" (an int), and "name" (a char array of length 16).

Next, we create an instance of the struct using the StructInst() function, passing the struct type and values for each member.

To access a specific member of the struct, we use the StructMember() function to obtain a member locator. This function takes the struct type and the name of the member as arguments.

We then use the VecEx() function/operator with the struct instance and the member locator to read the current value of the member. In the provided code, we print the values of "x", "y", "z", and "name" using the say statement.

After that, we perform some modifications on the struct members. We use the VecEx() function/operator with the member locator to assign new values or perform arithmetic operations on the members. In this case, we add 20 to "x", divide "y" by 2, right-shift "z" by 4 bits, and assign a new string value to "name".

Finally, we print the modified values of the struct members using the VecEx() function/operator and the member locator.

The output of the code sample shows the initial values of the struct members, the modified values, and demonstrates how the VecEx() function/operator allows us to access and modify individual members of a struct.

In the provided code sample, we demonstrate how to work with a struct that contains a nested struct member.

my $type = Struct(q~
    struct {
        char[16] name;
        struct {
            int x;
            int y;
        } pt;
    } MyStruct;
~);

my $struct = StructInst($type, "Hello World", [77, 22]);

say VecEx($struct, StructMember($type, "name"));
printr VecEx($struct, StructMember($type, "pt"));

VecEx($struct, StructMember($type, "name")) = "Hello Kitty!";
VecEx($struct, StructMember($type, "pt")) = qww(x 100 y 200);

say VecEx($struct, StructMember($type, "name"));
printr VecEx($struct, StructMember($type, "pt"));

// PRINTS
// Hello World
// Array
// (
//     [x] => 77
//     [y] => 22
// )
// Hello Kitty!
// Array
// (
//     [x] => 100
//     [y] => 200
// )

We define a struct type named "MyStruct" that consists of two members: "name" (a char array of length 16) and "pt" (a nested struct with members "x" and "y" of type int).

Using the StructInst() function, we create an instance of the struct and provide values for each member. In this case, we pass the string "Hello World" for "name" and an array [77, 22] for "pt" during struct instantiation.

To access and modify the struct members, we use the VecEx() function/operator with the member locators obtained from the StructMember() function.

We demonstrate accessing the values of "name" and "pt" using VecEx() and the appropriate member locators, and we print them using the say statement and the printr function.

We also show how to modify the values of "name" and "pt" by assigning new values. For "name", we simply assign a new string value "Hello Kitty!" using the VecEx() function/operator and the member locator. For "pt", we use the qww() function to create an associative array with key-value pairs representing the members "x" and "y", and assign it to "pt" using VecEx() and the member locator.

Finally, we print the updated values of "name" and "pt" using VecEx() and the appropriate member locators.

The output of the code sample demonstrates how to work with nested structs, access their members, and modify their values using the VecEx() function/operator and the appropriate member locators.

Same as above but using the StructDef function:

StructDef(q~
    struct Point{
        int x;
        int y;
    } Point;
~);

my $type = Struct(q~
    struct {
        char[16] name;
        struct Point pt;
    } MyStruct;
~);
my $struct = StructInst($type, "Hello World", [77, 22]);
ASSERT(1, VecEx($struct, StructMember($type, "name")) == "Hello World");
ASSERT(1, VecEx($struct, StructMember($type, "pt")) == qww(x 77 y 22));
VecEx($struct, StructMember($type, "name")) = "Hello Kitty!";
VecEx($struct, StructMember($type, "pt")) = qww(x 100 y 200);
ASSERT(1, VecEx($struct, StructMember($type, "name")) == "Hello Kitty!");
ASSERT(1, VecEx($struct, StructMember($type, "pt")) == qww(x 100 y 200));

A fairly complex example of nested structs:

StructDef(q~
    struct Size {
        int width;
        int height;
    };
~);

StructDef(q~
    struct Point {
        int x;
        int y;
    };
~);

StructDef(q~
    struct Bounds {
        struct Point pt;
        struct Size sz;
    };
~);

my $typeEntity = Struct(q~
    struct {
        struct Bounds bounds;
    } Entity;
~);

my $p = StructInst($typeEntity, [[100, 200], [50, 60]]);

say "X = " + VecEx($p, StructMember($typeEntity, "bounds.pt.x"));
say "Y = " + VecEx($p, StructMember($typeEntity, "bounds.pt.y"));
say "Width = " + VecEx($p, StructMember($typeEntity, "bounds.sz.width"));
say "Height = " + VecEx($p, StructMember($typeEntity, "bounds.sz.height"));

VecEx($p, StructMember($typeEntity, "bounds.pt.x")) = 10;
VecEx($p, StructMember($typeEntity, "bounds.pt.y")) = 11;
VecEx($p, StructMember($typeEntity, "bounds.sz.width")) = 20;
VecEx($p, StructMember($typeEntity, "bounds.sz.height")) = 21;

say "X = " + VecEx($p, StructMember($typeEntity, "bounds.pt.x"));
say "Y = " + VecEx($p, StructMember($typeEntity, "bounds.pt.y"));
say "Width = " + VecEx($p, StructMember($typeEntity, "bounds.sz.width"));
say "Height = " + VecEx($p, StructMember($typeEntity, "bounds.sz.height"));

printr FromStruct($typeEntity, $p);
say $p[<>];


// PRINTS
// 100
// 200
// 50
// 60
// 10
// 11
// 20
// 21
// Array
// (
//     [bounds] => Array
//         (
//             [pt] => Array
//                 (
//                     [x] => 10
//                     [y] => 11
//                 )
//             [sz] => Array
//                 (
//                     [width] => 20
//                     [height] => 21
//                 )
//         )
// )
// 0A 00 00 00 0B 00 00 00 14 00 00 00 15 00 00 00 ................

This code demonstrates how to read nested members in a struct using the Sputnik language. Here's a simplified explanation:

In summary, this code demonstrates how to define nested structs, instantiate them, read nested member values, modify them, and convert the struct back to its binary representation.

Here we use the array accessor [] to travel inside an array mid nesting:

my $typeEntity = Struct(q~
    struct Size {
        int width;
        int height;
    };
    struct Point {
        int x;
        int y;
    };
    struct Bounds {
        struct Point pt;
        struct Size[2] sz;
    };
    struct {
        struct Bounds bounds;
    } Entity;
~);
my $p = StructInst($typeEntity, [[100, 200], [[50, 60], [70, 80]]]);

say "X = " + VecEx($p, StructMember($typeEntity, "bounds.pt.x"));
say "Y = " + VecEx($p, StructMember($typeEntity, "bounds.pt.y"));
say "Width[0] = " + VecEx($p, StructMember($typeEntity, "bounds.sz[0].width"));
say "Height[0] = " + VecEx($p, StructMember($typeEntity, "bounds.sz[0].height"));
say "Width[1] = " + VecEx($p, StructMember($typeEntity, "bounds.sz[1].width"));
say "Height[1] = " + VecEx($p, StructMember($typeEntity, "bounds.sz[1].height"));

printr FromStruct($typeEntity, $p);
say $p[<>];

// PRINTS
// 100
// 200
// 50
// 60
// 70
// 80
// Array
// (
//     [bounds] => Array
//         (
//             [pt] => Array
//                 (
//                     [x] => 100
//                     [y] => 200
//                 )
//             [sz] => Array
//                 (
//                     [0] => Array
//                         (
//                             [width] => 50
//                             [height] => 60
//                         )
//                     [1] => Array
//                         (
//                             [width] => 70
//                             [height] => 80
//                         )
//                 )
//         )
// )
// 64 00 00 00 C8 00 00 00 32 00 00 00 3C 00 00 00 d...È...2...<...
// 46 00 00 00 50 00 00 00 -- -- -- -- -- -- -- -- F...P...

In this code snippet, we define a struct hierarchy that includes arrays as members. We create an instance of the top-level struct and initialize its members. Then, we use the VecEx function along with StructMember to access and retrieve specific values from the struct, including values from nested structs and array elements using the [ ] index notation.

The printed output demonstrates how we can successfully traverse the struct hierarchy and access the desired values using the specified member paths. It showcases the ability of Sputnik to handle complex struct compositions and provide convenient access to individual struct members and array elements.

Overall, this example illustrates the capability of Sputnik to effectively navigate and manipulate nested structs and arrays within a larger struct hierarchy.

Sometimes you may not wish to read a struct into a Sputnik array but just want it's raw binary:

my $pyRect = Struct(q~
    struct PACKED Point {
        int x;
        int y;
    };
    struct PACKED Size {
        int w;
        int h;
    };
    struct PACKED {
        struct Point p;
        struct Size sz;
    } Rect;
~);

my $p = StructInst($pyRect, [100, 200], [300, 400]);

my $rawP = VecEx($p, StructMember($pyRect, "p"), @VexRaw);

printr VecEx($p, StructMember($pyRect, "p"));

BinaryWriteInt32($rawP, 0, 777);
VecEx($p, StructMember($pyRect, "p")) = $rawP;

printr VecEx($p, StructMember($pyRect, "p"));

// PRINTS
// Array
// (
//     [x] => 100
//     [y] => 200
// )
// Array
// (
//     [x] => 777
//     [y] => 200
// )

Explanation of what's happening in the provided code:

In summary, the code demonstrates how to read a struct as raw binary data, modify a specific value within that binary data, and then save the modified binary data back into the struct. This also works on any type you read it will return as binary regardless of what it is be it a struct/integer/string/array etc.

To further explain this:

my $pyRect = Struct(q~
    struct PACKED {
        int a;
        double b;
        char[12] c;
        int[4] d;
    } Rect;
~);

my $p = StructInst($pyRect, 100, 500, "Hello!", [0x10, 0x20, 0x30, 0x40]);

say "A=" . VecEx($p, StructMember($pyRect, "a"), @VexRaw)[<>];
say "B=" . VecEx($p, StructMember($pyRect, "b"), @VexRaw)[<>];
say "C=" . VecEx($p, StructMember($pyRect, "c"), @VexRaw)[<>];
say "D=" . VecEx($p, StructMember($pyRect, "d"), @VexRaw)[<>];

// PRINTS
// A=64 00 00 00 -- -- -- -- -- -- -- -- -- -- -- -- d...
// B=00 00 00 00 00 40 7F 40 -- -- -- -- -- -- -- -- .....@.@
// C=48 65 6C 6C 6F 21 00 00 00 00 00 00 -- -- -- -- Hello!......
// D=10 00 00 00 20 00 00 00 30 00 00 00 40 00 00 00 .... ...0...@...

In the provided code, a packed struct named $pyRect is defined, consisting of four members: an integer a, a double b, a character array c, and an integer array d.

An instance of the struct is created using StructInst, initializing the values of a, b, c, and d.

Using VecEx with the @VexRaw flag, the raw binary representation of each member is extracted and printed.

The output demonstrates that in raw mode, the binary representation of each member is returned, including integers, floats, character arrays, and integer arrays etc.


Unknown Member Size

Unknown Member Size refers to a situation in which a member of a struct or array is defined with an indeterminate size using the [] notation. This primarily applies to the last member of a struct or array. It allows variable-length data to be accommodated after the defined elements within that particular member.

When an unknown member size is used for a non-terminal member, it may lead to issues with the overall struct record integrity. Since the size of the subsequent members cannot be determined, the struct's layout may become inconsistent and difficult to interpret correctly.

It's important to note that the unknown member size convention is primarily intended for the last member, where it allows for flexible data allocation based on the available space. Using it for non-terminal members can result in unpredictable behavior and potential data corruption.

To maintain the integrity of the struct record, it is recommended to either specify the size of each member explicitly or ensure that the data alignment and padding are appropriately accounted for. By defining the size of each member, you can ensure consistent memory layout and reliable access to the struct's elements.

In summary, while unknown member size ([]) can be used to accommodate variable-length data in the last member of a struct or array, it is not suitable for non-terminal members as it can compromise the struct's integrity. It is crucial to define the sizes of individual members to maintain a well-defined and consistent struct record.

Here is a demonstration of a flexible array at the end of a struct (variable length array):

my $ty = Struct(q~
struct { int age; char[] name; } v; ~); my $p = StructInst($ty, 7, "Kitty"); printr FromStruct($ty, $p); say VecEx($p, StructMember($ty, "age")); say VecEx($p, StructMember($ty, "name")); printr($p); // PRINTS // Array // ( // [age] => 7 // [name] => Kitty // ) // 7 // Kitty // 07 00 00 00 4B 69 74 74 79 00 ....Kitty.

In this example the name member of the struct is defined as a character array with no specific size, making it a "flexible array member." As the last member of the struct, the name member is treated as if it were a zero-sized array.

When you create an instance of the struct using StructInst, you pass the value "Kitty" for the name member. Sputnik will read and interpret this data based on the assumption that the name member exists and contains a null-terminated string.

If the name member were not provided or set to an empty string, the name field in the resulting struct would be an empty string as well.

It's important to note that when working with flexible array members, you should ensure that the data is properly allocated and accessible when accessing or manipulating the struct. If the data doesn't exist, attempting to access it may result in unexpected behavior or errors.

Here we make a variable length array in a struct and add values to it:

my $ty = Struct(q~
    struct {
        int val;
        int[] vals;
    } v;
~);
my $p = StructInst($ty, 7);
$p .= pack("iiii", 100, 200, 300, 400);
say VecEx($p, StructMember($ty, "val"));
printr VecEx($p, StructMember($ty, "vals"));
// PRINTS
// 7
// Array
// (
//     [0] => 100
//     [1] => 200
//     [2] => 300
//     [3] => 400
// )

In this example, we manually append integers to a variable-length array in a struct. The struct has two members: "val" and "vals". After creating the struct instance, we use the pack function to add integers to the "vals" member. We can then retrieve the values and observe that the "val" member has a value of 7, while the "vals" member contains the appended integers. This showcases the flexibility of variable-length arrays in structs, allowing us to dynamically extend them as needed.

Here multiple integers are added to the end of the struct data to fill up the variable length array:

my $ty = Struct(q~
    struct {
        int val;
        int[] vals;
    } v;
~);
my $p = StructInst($ty, 7);
$p .= (Binary)arrayOf(@TypeInt32, 100, 200, 300, 400);
$p .= (Binary)arrayOf(@TypeInt32, 77, 42);
say VecEx($p, StructMember($ty, "val"));
printr VecEx($p, StructMember($ty, "vals"));
// PRINTS
// 7
// Array
// (
//     [0] => 100
//     [1] => 200
//     [2] => 300
//     [3] => 400
//     [4] => 77
//     [5] => 42
// )

In this example, we demonstrate the ability to keep adding elements to a variable-length array in a struct. The struct has two members: "val" and "vals". We initialize the struct instance with a value of 7. Then, using the (Binary)arrayOf function, we append two arrays of integers to the "vals" member. The resulting struct contains the original "val" value of 7 and the extended "vals" array, which includes the values 100, 200, 300, 400, 77, and 42. This showcases the dynamic nature of variable-length arrays in structs, allowing us to continuously expand the array as needed.

Here Struct() is used for creating data to be used as the variable length array:

my $v = Struct(q(int test[] = {100, 200, 300, 400}));
my $ty = Struct(q~
    struct {
        int val;
        int[] vals;
    } v;
~);
my $p = StructInst($ty, 7) . $v;
say VecEx($p, StructMember($ty, "val"));
printr VecEx($p, StructMember($ty, "vals"));
// PRINTS
// 7
// Array
// (
//     [0] => 100
//     [1] => 200
//     [2] => 300
//     [3] => 400
// )

In this example, we demonstrate the usage of the Struct function to create arrays within a struct. We define a struct with two members: "val" of type int and "vals" of type int[]. The "vals" member is initialized with the values {100, 200, 300, 400} using the Struct function. We then create an instance of the struct with the "val" value set to 7 and append the "vals" array to it. The resulting struct contains the original "val" value of 7 and the "vals" array with the values {100, 200, 300, 400}. This showcases how the Struct function can be used to create variable-length arrays within a struct and how they can be appended to the end of the struct instance.

Making a flexible array of structs:

my $v = Struct(q~
    struct POINT {
        int x;
        int y;
    };
    struct POINT v[] = {{10, 20}, {30, 40}, {50, 60}, {70, 80}};
~);
my $ty = Struct(q~
    struct POINT {
        int x;
        int y;
    };
    struct {
        int val;
        struct POINT[] vals;
    } v;
~);
my $p = StructInst($ty, 7) . $v;
printr VecEx($p, StructMember($ty, "vals"));
// PRINTS
// Array // ( // [0] => Array // ( // [x] => 10 // [y] => 20 // ) // [1] => Array // ( // [x] => 30 // [y] => 40 // ) // [2] => Array // ( // [x] => 50 // [y] => 60 // ) // [3] => Array // ( // [x] => 70 // [y] => 80 // ) // )

In this example, we define a variable-length array of structs using the struct keyword in Sputnik. The array consists of POINT structs, each representing a point with x and y coordinates. We create an instance of another struct that includes an int member and a variable-length array of POINT structs. By concatenating the initialized array with the struct instance, we can access and print the values of the variable-length array.

A flexible array of structs but demonstrating modifying one of them even using operators:

my $v = Struct(q~
    struct POINT {
        int x;
        int y;
    };
    struct POINT v[] = {{10, 20}, {30, 40}};
~);
my $ty = Struct(q~
    struct POINT {
        int x;
        int y;
    };
    struct {
        int val;
        struct POINT[] vals;
    } v;
~);
my $p = StructInst($ty, 7) . $v;
VecEx($p, StructMember($ty, "vals[0]")) += array(10, 20);
VecEx($p, StructMember($ty, "vals[1]")) = array(100, 200);
printr VecEx($p, StructMember($ty, "vals"));
// PRINTS
// Array
// (
//     [0] => Array
//         (
//             [x] => 20
//             [y] => 40
//         )
//     [1] => Array
//         (
//             [x] => 100
//             [y] => 200
//         )
// )

In this example, we have a variable-length array of POINT structs. We create an instance of a struct that includes an int member and a variable-length array of POINT structs. After initializing the struct, we access individual POINT structs within the array using the StructMember function. We can modify the values of these structs by assigning new values or using the += operator to modify values. Finally, we print the updated array of POINT structs.

Same as above but this time creating an empty space to add a new element later:

my $v = Struct(q~
    struct POINT {
        int x;
        int y;
    };
    struct POINT v[] = {{10, 20}, {30, 40}, {,}};
~);
my $ty = Struct(q~
    struct POINT {
        int x;
        int y;
    };
    struct {
        int val;
        struct POINT[] vals;
    } v;
~);
my $p = StructInst($ty, 7) . $v;
VecEx($p, StructMember($ty, "vals[0]")) += array(10, 20);
VecEx($p, StructMember($ty, "vals[2]")) = array(100, 200);
printr VecEx($p, StructMember($ty, "vals"));
// PRINTS
// Array
// (
//     [0] => Array
//         (
//             [x] => 20
//             [y] => 40
//         )
//     [1] => Array
//         (
//             [x] => 30
//             [y] => 40
//         )
//     [2] => Array
//         (
//             [x] => 100
//             [y] => 200
//         )
// )

In the updated code snippet, a crucial change was introduced by including an "empty" element {,} in the definition of the variable-length array of POINT structs. This "empty" element serves as a placeholder within the array, creating space for a value to be filled in later. The array, with the addition of the "empty" element, becomes more flexible, allowing developers to dynamically add values to specific positions within the array. This flexibility is showcased when creating an instance of the struct type, where the "empty" element in the array is later populated with specific values using the VecEx function. Overall, the inclusion of the "empty" element enhances the adaptability of the array, enabling dynamic modification of its contents during runtime.

Same as above but this time creating a macro to add a new elements later:

#define NxtPoint(x, y) {{return Struct(q~struct POINT { int x; int y; }; struct POINT v = {~ . x . ", ". y . q~};~);}}()

my $v = Struct(q~
    struct POINT {
        int x;
        int y;
    };
    struct POINT v[] = {{10, 20}, {30, 40}, {,}};
~);
my $ty = Struct(q~
    struct POINT {
        int x;
        int y;
    };
    struct {
        int val;
        struct POINT[] vals;
    } v;
~);
my $p = StructInst($ty, 7) . $v;
VecEx($p, StructMember($ty, "vals[0]")) += array(10, 20);
VecEx($p, StructMember($ty, "vals[2]")) = array(100, 200);
$p .= NxtPoint(90, 70);
$p .= NxtPoint(400, 500);
printr VecEx($p, StructMember($ty, "vals"));
// PRINTS
// Array
// (
//     [0] => Array
//         (
//             [x] => 20
//             [y] => 40
//         )
//     [1] => Array
//         (
//             [x] => 30
//             [y] => 40
//         )
//     [2] => Array
//         (
//             [x] => 100
//             [y] => 200
//         )
//     [3] => Array
//         (
//             [x] => 90
//             [y] => 70
//         )
//     [4] => Array
//         (
//             [x] => 400
//             [y] => 500
//         )
// )

The introduced macro, defined as NxtPoint(x, y), streamlines the creation of new POINT structs. This macro takes x and y coordinates as parameters and generates a new POINT struct with the specified values. By utilizing this macro, the process of defining and initializing new POINT structs becomes concise and modular. In the updated code, the macro is invoked twice to append two additional POINT structs, (90, 70) and (400, 500), to the existing array of structs. This approach enhances code readability and promotes code reusability, allowing developers to easily extend the array with new POINT structs by invoking the macro with different coordinate values. The result is a more efficient and maintainable way to produce and append structs within the array, demonstrating the practicality of using macros for repetitive and modular code generation in the Sputnik language.

Of course the macro could be defined in a more easy to read way:

#macro NxtPoint(x, y)
{{
    return Struct(q~
        struct POINT {
            int x;
            int y;
        };
        struct POINT v = {~ . x . ", ". y . q~};
    ~);
}}()
#endm

This written as #macro NxtPoint(x, y) ... #endm, offers a more readable and easily modifiable alternative compared to the traditional #define syntax. By encapsulating the macro body within the #macro ... #endm structure, the code benefits from a more structured and visually intuitive format. This approach enhances readability and maintainability, making it simpler for developers to comprehend and modify the macro's functionality. Unlike the somewhat cryptic nature of #define, the #macro ... #endm syntax clearly delineates the beginning and end of the macro, providing a more straightforward and visually cohesive way to define reusable code snippets. This improved readability facilitates the comprehension of the macro's purpose and parameters, making it easier for developers to modify or extend its functionality as needed.

Using TypeDef we can avoid having to recreate the struct each time:

TypeDef(q~
    struct  {
        int x;
        int y;
    } POINT;
~);

my $v = Struct(q~
    POINT v[] = {{10, 20}, {30, 40}, {,}};
~);
my $ty = Struct(q~
    struct {
        int val;
        POINT[] vals;
    } v;
~);
my $p = StructInst($ty, 7) . $v;
VecEx($p, StructMember($ty, "vals[0]")) += array(10, 20);
VecEx($p, StructMember($ty, "vals[2]")) = array(100, 200);
$p .= TypeDefInst("POINT", 90, 70);
$p .= (POINT)array(250, 350);
$p .= (POINT)[400, 500];
printr VecEx($p, StructMember($ty, "vals"));
// PRINTS
// Array
// (
//     [0] => Array
//         (
//             [x] => 20
//             [y] => 40
//         )
//     [1] => Array
//         (
//             [x] => 30
//             [y] => 40
//         )
//     [2] => Array
//         (
//             [x] => 100
//             [y] => 200
//         )
//     [3] => Array
//         (
//             [x] => 90
//             [y] => 70
//         )
//     [4] => Array
//         (
//             [x] => 250
//             [y] => 350
//         )
//     [5] => Array
//         (
//             [x] => 400
//             [y] => 500
//         )
// )

In this refined Sputnik script, the use of TypeDef and Struct simplifies the creation and manipulation of a dynamic array of POINT structs. The script begins by defining a POINT struct using TypeDef, followed by the creation of an array of POINT structs using the Struct construct. The custom type v is then defined to represent a structure containing an integer value and an array of POINT structs.

With the dynamic array structure in place, the script proceeds to instantiate an object of type v and initializes it with a combination of predefined arrays, direct casting, and direct initialization of the POINT struct. Notably, the script showcases the flexibility of working with arrays, allowing them to be directly cast as POINT structs in either the [] or array() form.

The VecEx and printr calls at the end demonstrate how easily the elements of the created object can be accessed and printed, showcasing the efficiency and simplicity achieved through the use of TypeDef and Struct. Overall, this approach eliminates the need for macros and streamlines the code, making it more readable and concise while still maintaining the flexibility to work with custom data types and structures.


Automatic Translation

Structs in Sputnik are automatically translated to and from Sputnik variables. While structs don't consist of Sputnik variables directly, you can read from and write to them as if they were Sputnik variables. This allows for easy integration of binary data into Sputnik code.

This feature allows structs to be automatically translated to and from Sputnik variables. Although structs in Sputnik are binary blobs, they can be accessed and manipulated as if they were Sputnik variables. This simplifies the process of working with binary data and enables seamless integration with Sputnik code.


Summary

Please note that working with structs requires a good understanding of the struct definition syntax, member access using VecEx() and StructMember(), and handling binary data.

Make sure to go read Structs (Union) since they are very handy and go well with Structs.

Make sure to go read TypeDefs since that expands on Structs by allowing them to be used as function arguments and function return values and even casts.


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