"Sputnik" help  
Sputnik Help
Language Reference - Datatypes - Structs (Union)

Structs (Union)


Prerequisites

Before diving into the Struct (Union) system, it is important to have a solid understanding of the Struct system in Sputnik. If you haven't already, please refer to the Struct documentation to familiarize yourself with the basics of creating and working with structs.

Do not proceed down this document until you at least understand Structs properly.


About Unions

Unions in programming are a data structure that allows multiple variables of different types to share the same memory space. In a union, only one member can be active at a time, and modifying one member affects the others due to their overlapping memory.

When working with unions in Sputnik, you can define a union using the Struct() function, specifying its members and their respective data types. For example, a union may have an int member and a float member:

my $type = Struct(q~
    union {
        int intValue;
        float floatValue;
    } MyUnion;
~);

To read or modify the value of a specific member in a union, you can use the StructMember() function to obtain a member locator. Then, you can use the VecEx() function/operator to access the desired member. For instance, to read the value of the intValue member:

say VecEx($union, StructMember($type, "intValue"));

Similarly, you can modify the value of the floatValue member:

VecEx($union, StructMember($type, "floatValue")) = 3.14;

Remember that when using unions, only one member can be active at a time. Modifying a member will affect the other members in the union due to their shared memory space.

Unions provide flexibility in handling different types of data within a single variable, allowing for efficient memory usage. However, it is crucial to be cautious when accessing and modifying union members to ensure consistency and avoid unexpected behavior.

Note: It's essential to have a solid understanding of the Struct system in Sputnik before delving into unions. Refer to the Struct documentation for a comprehensive overview of working with structs

Putting it all together:

my $type = Struct(q~
    union {
        int intValue;
        float floatValue;
    } MyUnion;
~);

my $union = StructInst($type);

VecEx($union, StructMember($type, "intValue")) = 777;
say "IntValue = " . VecEx($union, StructMember($type, "intValue"));
say "FloatValue = " . VecEx($union, StructMember($type, "floatValue"));

VecEx($union, StructMember($type, "floatValue")) = 3.14;
say "IntValue = " . VecEx($union, StructMember($type, "intValue"));
say "FloatValue = " . VecEx($union, StructMember($type, "floatValue"));

// PRINTS
// IntValue = 777
// FloatValue = 1.088809E-42
// IntValue = 1078523331
// FloatValue = 3.14

In this example, we create a union using the Struct function in Sputnik. The union consists of two members: intValue of type int and floatValue of type float. The StructInst function is then used to instantiate the union.

We access the members of the union using the StructMember function, which returns the offset and size of the specified member within the union. We use the VecEx function to read and modify the values of the union members.

Initially, we set the value of intValue to 777 using VecEx. When we print the values of intValue and floatValue, we see that intValue is indeed 777, but floatValue displays a seemingly unrelated value. This is because the memory is shared between the members, and interpreting the binary representation of 777 as a float leads to a different value.

Next, we set the value of floatValue to 3.14 using VecEx. When we print the values of intValue and floatValue again, we observe that intValue has changed to 1078523331, the binary representation of the float value 3.14. The shared memory space of the union allows modifying and accessing different types without explicitly converting them.

This behavior is possible because unions share the memory space among their members, enabling different interpretations of the same underlying data. However, it's crucial to exercise caution and ensure that only one member is actively accessed or modified at a time to avoid unexpected results.


Examples

A basic example:

my $type = Struct(q~
    union PACKED {
        struct PACKED {
            int x;
            int y;
        };
        long w;
    } MyUnion;
~);

my $union = StructInst($type);


VecEx($union, StructMember($type, "x")) = 111;
VecEx($union, StructMember($type, "y")) = 222;

say "X = " . VecEx($union, StructMember($type, "x"));
say "Y = " . VecEx($union, StructMember($type, "y"));
say "W = " . VecEx($union, StructMember($type, "w"));


// PRINTS
// X = 111
// Y = 222
// W = 953482739823

The code demonstrates the usage of an anonymous union within a struct in Sputnik. The PACKED attribute is applied to both the struct and the union to ensure tight packing without any padding between the members.

In this example, the union contains an anonymous struct with two integer members, x and y, and a long integer member w. By assigning values to x and y and accessing w, the code showcases how the union allows accessing the same memory space as different types.

The code highlights the versatility and flexibility of unions in Sputnik, where you can define different types within the same memory space, enabling efficient memory usage and handling of different data representations.

It's worth noting that the usage of the `w` member within the union allows for a compact representation of the `x` and `y` values. Since `w` is a 64-bit long integer, while `x` and `y` are 32-bit integers, setting `w` automatically sets both `x` and `y` to the same value.

This feature provides an easy and efficient way to save and retrieve the values of `x` and `y` by simply accessing the `w` member. It eliminates the need for separate handling of `x` and `y` when storing or retrieving the packed representation.

This capability is particularly useful when dealing with memory-constrained environments or when optimizing data storage and transmission, as it allows for reduced memory footprint and simplified data handling.

A more indepth example where we can have a union containing red/green/blue etc values and save/load them from a single value:

my $type = Struct(q~
    union PACKED {
        struct PACKED {
            uint8_t alpha;
            uint8_t red;
            uint8_t green;
            uint8_t blue;
        };
        uint32_t data;
    } COLOR;
~);

my $union = StructInst($type);

VecEx($union, StructMember($type, "red")) = 100;
VecEx($union, StructMember($type, "green")) = 150;
VecEx($union, StructMember($type, "blue")) = 255;

say "Values:";
say "Red = " . VecEx($union, StructMember($type, "red"));
say "Green = " . VecEx($union, StructMember($type, "green"));
say "Blue = " . VecEx($union, StructMember($type, "blue"));
say;

my $saved = VecEx($union, StructMember($type, "data"));

VecEx($union, StructMember($type, "red")) = 1;
VecEx($union, StructMember($type, "green")) = 2;
VecEx($union, StructMember($type, "blue")) = 3;

say "Values (After Changing):";
say "Red = " . VecEx($union, StructMember($type, "red"));
say "Green = " . VecEx($union, StructMember($type, "green"));
say "Blue = " . VecEx($union, StructMember($type, "blue"));
say;

VecEx($union, StructMember($type, "data")) = $saved;

say "Values (Back):";
say "Red = " . VecEx($union, StructMember($type, "red"));
say "Green = " . VecEx($union, StructMember($type, "green"));
say "Blue = " . VecEx($union, StructMember($type, "blue"));
say;

say "Saved value was $saved";

// PRINTS
// Values:
// Red = 100
// Green = 150
// Blue = 255
// 
// Values (After Changing):
// Red = 1
// Green = 2
// Blue = 3
// 
// Values (Back):
// Red = 100
// Green = 150
// Blue = 255
// 
// Saved value was 4288046080

This code demonstrates the usage of a union called COLOR, which combines a struct with individual color components (alpha, red, green, blue) and a single 32-bit data value.

Initially, the individual color components are set to specific values. Then, the data value is retrieved from the union, effectively packing the color components into a single 32-bit integer.

Next, the color components are modified to different values. However, when the data value is restored to the union, the original color values are automatically restored as well. This showcases the ability to conveniently store and retrieve a group of related values using the data member.

Overall, the union allows for the flexible representation and manipulation of color data, enabling efficient storage and convenient access to individual components or a packed representation.

A fairly complex example:

my $type = Struct(q~
    union {
        int intValue;
        float floatValue;
        char[5] magicValue;
    } MyUnion;
~);

my $union = StructInst($type);

VecEx($union, StructMember($type, "magicValue")) = "PDF.";
say "IntValue = " . VecEx($union, StructMember($type, "intValue"));
say "FloatValue = " . VecEx($union, StructMember($type, "floatValue"));
say "MagicValue = " . VecEx($union, StructMember($type, "magicValue"));
say "MagicValue[0] = " . VecEx($union, StructMember($type, "magicValue[0]"));
say "MagicValue[1] = " . VecEx($union, StructMember($type, "magicValue[1]"));
say "MagicValue[2] = " . VecEx($union, StructMember($type, "magicValue[2]"));


// PRINTS
// IntValue = 776356944
// FloatValue = 4.508066E-11
// MagicValue = PDF.
// MagicValue[0] = P
// MagicValue[1] = D
// MagicValue[2] = F

In this example, we have a union called MyUnion defined within a struct. The union consists of three members: intValue, floatValue, and magicValue. Each member shares the same memory space within the union, allowing them to be accessed interchangeably.

By using VecEx and StructMember, we can access and modify the values of the union members. In the code snippet, we set the value of magicValue to "PDF." using VecEx. We then retrieve and display the values of intValue, floatValue, and magicValue using VecEx as well.

The output shows the interpretation of the shared memory space. The intValue and floatValue represent the binary interpretation of the modified magicValue string. Similarly, we can access individual characters of magicValue using the syntax magicValue[index] with VecEx, providing the ability to manipulate individual bytes or characters within the union.

This example demonstrates the flexibility of Sputnik's struct system and the power of VecEx to handle different data types within unions. It allows for seamless interaction between different data representations, enabling read/write operations on integers, floats, strings, and individual characters/bytes within the shared memory space of the union.


Summary

Unions in Sputnik provide a powerful mechanism for defining data structures where multiple variables of different types can share the same memory space. With unions, only one member can be active at a time, and modifying one member affects the others due to their overlapping memory.

By using the Struct() function, you can define unions and their members, specifying the data types for each member. The StructMember() function allows you to obtain a member locator, which can be used with the VecEx() function/operator to read or modify the values of specific union members.

Unions in Sputnik offer flexibility in handling different types of data within a single variable, allowing for efficient memory usage. They can be particularly useful in scenarios where you need to switch between different data types or represent a single piece of data in various formats.

It's important to exercise caution when working with unions to ensure that only one member is actively modified at a time, as modifying one member will affect the others due to their shared memory space.

Overall, unions in Sputnik provide a versatile tool for working with complex data structures and performing low-level operations. Their integration with the Struct system and the extensive binary manipulation capabilities of Sputnik make them a valuable feature for handling diverse data representations.


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