12.2 MemorySegment Operations and Data Access

Master efficient data access patterns with MemorySegment for high-performance memory manipulation.

Basic Data Access

Reading and Writing Primitives:

import java.lang.foreign.*;

public class PrimitiveAccess {
    public void demonstrateAccess() {
        try (Arena arena = Arena.ofConfined()) {
            MemorySegment segment = arena.allocate(64);

            // Write primitives at different offsets
            segment.set(ValueLayout.JAVA_BYTE, 0, (byte) 127);
            segment.set(ValueLayout.JAVA_SHORT, 2, (short) 32000);
            segment.set(ValueLayout.JAVA_INT, 4, 42);
            segment.set(ValueLayout.JAVA_LONG, 8, 123456789L);
            segment.set(ValueLayout.JAVA_FLOAT, 16, 3.14f);
            segment.set(ValueLayout.JAVA_DOUBLE, 20, 2.718);
            segment.set(ValueLayout.JAVA_CHAR, 28, 'A');
            segment.set(ValueLayout.JAVA_BOOLEAN, 30, true);

            // Read back
            byte b = segment.get(ValueLayout.JAVA_BYTE, 0);
            short s = segment.get(ValueLayout.JAVA_SHORT, 2);
            int i = segment.get(ValueLayout.JAVA_INT, 4);
            long l = segment.get(ValueLayout.JAVA_LONG, 8);
            float f = segment.get(ValueLayout.JAVA_FLOAT, 16);
            double d = segment.get(ValueLayout.JAVA_DOUBLE, 20);
            char c = segment.get(ValueLayout.JAVA_CHAR, 28);
            boolean bool = segment.get(ValueLayout.JAVA_BOOLEAN, 30);

            System.out.printf("byte=%d, short=%d, int=%d, long=%d\n", b, s, i, l);
            System.out.printf("float=%f, double=%f, char=%c, bool=%b\n", f, d, c, bool);
        }
    }
}

Array Access:

public class ArrayAccess {
    public void intArrayAccess() {
        try (Arena arena = Arena.ofConfined()) {
            int[] values = {10, 20, 30, 40, 50};

            // Copy Java array to native memory
            MemorySegment segment = arena.allocateArray(
                ValueLayout.JAVA_INT,
                values
            );

            // Read individual elements
            for (int i = 0; i < values.length; i++) {
                long offset = i * ValueLayout.JAVA_INT.byteSize();
                int value = segment.get(ValueLayout.JAVA_INT, offset);
                System.out.println("Element " + i + ": " + value);
            }

            // Modify element
            segment.set(ValueLayout.JAVA_INT, 2 * 4, 300);  // Third element

            // Copy back to Java array
            int[] result = segment.toArray(ValueLayout.JAVA_INT);
            System.out.println("Modified: " + java.util.Arrays.toString(result));
        }
    }

    public void byteArrayAccess() {
        try (Arena arena = Arena.ofConfined()) {
            byte[] data = "Hello, MemorySegment!".getBytes();

            MemorySegment segment = arena.allocateArray(
                ValueLayout.JAVA_BYTE,
                data
            );

            // Read as byte array
            byte[] copy = segment.toArray(ValueLayout.JAVA_BYTE);
            System.out.println("Data: " + new String(copy));

            // Direct byte access
            for (long i = 0; i < segment.byteSize(); i++) {
                byte b = segment.get(ValueLayout.JAVA_BYTE, i);
                System.out.printf("%c", (char) b);
            }
            System.out.println();
        }
    }
}

Slicing and Segmentation

Creating Slices:

public class SlicingOperations {
    public void demonstrateSlicing() {
        try (Arena arena = Arena.ofConfined()) {
            // Allocate large buffer
            MemorySegment buffer = arena.allocate(1024);

            // Create slices for different regions
            MemorySegment header = buffer.asSlice(0, 64);
            MemorySegment data = buffer.asSlice(64, 896);
            MemorySegment footer = buffer.asSlice(960, 64);

            // Use slices independently
            header.set(ValueLayout.JAVA_INT, 0, 0xABCD);  // Magic number
            data.set(ValueLayout.JAVA_LONG, 0, System.currentTimeMillis());
            footer.set(ValueLayout.JAVA_INT, 0, 0x1234);  // Checksum

            // Slices share same memory
            int magic = buffer.get(ValueLayout.JAVA_INT, 0);
            System.out.println("Magic: 0x" + Integer.toHexString(magic));
        }
    }

    public void nestedSlicing() {
        try (Arena arena = Arena.ofConfined()) {
            MemorySegment buffer = arena.allocate(256);

            // First level slices
            MemorySegment firstHalf = buffer.asSlice(0, 128);
            MemorySegment secondHalf = buffer.asSlice(128, 128);

            // Further slice first half
            MemorySegment quarter1 = firstHalf.asSlice(0, 64);
            MemorySegment quarter2 = firstHalf.asSlice(64, 64);

            // All reference same underlying memory
            quarter1.set(ValueLayout.JAVA_INT, 0, 100);

            // Can read from buffer, firstHalf, or quarter1
            int value = buffer.get(ValueLayout.JAVA_INT, 0);
            System.out.println("Value: " + value);  // 100
        }
    }
}

Copying Operations

Segment-to-Segment Copy:

public class CopyOperations {
    public void segmentCopy() {
        try (Arena arena = Arena.ofConfined()) {
            // Source segment
            MemorySegment source = arena.allocate(100);
            for (int i = 0; i < 25; i++) {
                source.set(ValueLayout.JAVA_INT, i * 4L, i);
            }

            // Destination segment
            MemorySegment dest = arena.allocate(100);

            // Copy entire segment
            MemorySegment.copy(source, 0, dest, 0, 100);

            // Verify
            for (int i = 0; i < 25; i++) {
                int value = dest.get(ValueLayout.JAVA_INT, i * 4L);
                System.out.println("Copied[" + i + "]: " + value);
            }
        }
    }

    public void partialCopy() {
        try (Arena arena = Arena.ofConfined()) {
            MemorySegment source = arena.allocate(1024);
            MemorySegment dest = arena.allocate(1024);

            // Fill source
            for (int i = 0; i < 256; i++) {
                source.set(ValueLayout.JAVA_INT, i * 4L, i);
            }

            // Copy middle section (bytes 256-767)
            MemorySegment.copy(
                source, 256,  // Source offset
                dest, 0,      // Dest offset
                512           // Length in bytes
            );

            // Verify copied section
            int firstCopied = dest.get(ValueLayout.JAVA_INT, 0);
            System.out.println("First copied value: " + firstCopied);  // Should be 64
        }
    }
}

Java Array Integration:

public class ArrayIntegration {
    public void javaToNative() {
        try (Arena arena = Arena.ofConfined()) {
            int[] javaArray = {1, 2, 3, 4, 5};

            // Allocate native memory
            MemorySegment segment = arena.allocate(
                ValueLayout.JAVA_INT.byteSize() * javaArray.length
            );

            // Copy Java array to native memory
            MemorySegment.copy(
                javaArray, 0,
                segment, ValueLayout.JAVA_INT, 0,
                javaArray.length
            );

            // Verify
            for (int i = 0; i < javaArray.length; i++) {
                int value = segment.get(ValueLayout.JAVA_INT, i * 4L);
                System.out.println("Native[" + i + "]: " + value);
            }
        }
    }

    public void nativeToJava() {
        try (Arena arena = Arena.ofConfined()) {
            // Create and populate native memory
            MemorySegment segment = arena.allocate(20);  // 5 ints
            for (int i = 0; i < 5; i++) {
                segment.set(ValueLayout.JAVA_INT, i * 4L, (i + 1) * 10);
            }

            // Copy to Java array
            int[] javaArray = new int[5];
            MemorySegment.copy(
                segment, ValueLayout.JAVA_INT, 0,
                javaArray, 0,
                5
            );

            System.out.println("Java array: " + java.util.Arrays.toString(javaArray));
        }
    }
}

Bulk Operations

Fill Operations:

public class BulkOperations {
    public void fillSegment() {
        try (Arena arena = Arena.ofConfined()) {
            MemorySegment segment = arena.allocate(1024);

            // Fill with zeros
            segment.fill((byte) 0);

            // Fill with pattern
            segment.fill((byte) 0xFF);

            // Verify
            byte b = segment.get(ValueLayout.JAVA_BYTE, 500);
            System.out.println("Filled byte: 0x" + Integer.toHexString(b & 0xFF));
        }
    }

    public void compareSegments() {
        try (Arena arena = Arena.ofConfined()) {
            MemorySegment seg1 = arena.allocate(100);
            MemorySegment seg2 = arena.allocate(100);

            // Fill both
            seg1.fill((byte) 42);
            seg2.fill((byte) 42);

            // Compare using mismatch
            long mismatchOffset = seg1.mismatch(seg2);

            if (mismatchOffset == -1) {
                System.out.println("Segments are identical");
            } else {
                System.out.println("First mismatch at byte: " + mismatchOffset);
            }

            // Modify one byte
            seg2.set(ValueLayout.JAVA_BYTE, 50, (byte) 99);

            mismatchOffset = seg1.mismatch(seg2);
            System.out.println("Mismatch at: " + mismatchOffset);  // 50
        }
    }
}

String Operations

String Encoding/Decoding:

public class StringOperations {
    public void utf8Strings() {
        try (Arena arena = Arena.ofConfined()) {
            // Create UTF-8 C string
            String text = "Hello, 世界! 🌍";
            MemorySegment segment = arena.allocateUtf8String(text);

            // Read back
            String decoded = segment.getUtf8String(0);
            System.out.println("Decoded: " + decoded);

            // Check size (includes null terminator)
            long size = segment.byteSize();
            System.out.println("Segment size: " + size + " bytes");

            // Verify null terminator
            byte nullByte = segment.get(ValueLayout.JAVA_BYTE, size - 1);
            System.out.println("Null terminator: " + (nullByte == 0));
        }
    }

    public void stringArray() {
        try (Arena arena = Arena.ofConfined()) {
            String[] strings = {"First", "Second", "Third"};

            // Allocate array of pointers
            MemorySegment pointerArray = arena.allocate(
                ValueLayout.ADDRESS.byteSize() * strings.length
            );

            // Allocate each string and store pointer
            for (int i = 0; i < strings.length; i++) {
                MemorySegment str = arena.allocateUtf8String(strings[i]);
                pointerArray.set(
                    ValueLayout.ADDRESS,
                    i * ValueLayout.ADDRESS.byteSize(),
                    str
                );
            }

            // Read back strings
            for (int i = 0; i < strings.length; i++) {
                MemorySegment strPtr = pointerArray.get(
                    ValueLayout.ADDRESS,
                    i * ValueLayout.ADDRESS.byteSize()
                );
                String str = strPtr.getUtf8String(0);
                System.out.println("String[" + i + "]: " + str);
            }
        }
    }
}

Memory-Mapped Files

Reading Memory-Mapped Files:

import java.nio.channels.FileChannel;
import java.nio.file.*;

public class MemoryMappedFiles {
    public void readMappedFile(Path path) throws Exception {
        try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) {
            MemorySegment mapped = channel.map(
                FileChannel.MapMode.READ_ONLY,
                0,
                channel.size(),
                Arena.ofConfined()
            );

            // Read file header
            int magic = mapped.get(ValueLayout.JAVA_INT, 0);
            int version = mapped.get(ValueLayout.JAVA_INT, 4);

            System.out.printf("Magic: 0x%X, Version: %d\n", magic, version);

            // Read data efficiently - no copying
            long fileSize = mapped.byteSize();
            System.out.println("File size: " + fileSize + " bytes");
        }
    }

    public void writeMappedFile(Path path, long size) throws Exception {
        try (FileChannel channel = FileChannel.open(
            path,
            StandardOpenOption.CREATE,
            StandardOpenOption.READ,
            StandardOpenOption.WRITE
        )) {
            try (Arena arena = Arena.ofConfined()) {
                MemorySegment mapped = channel.map(
                    FileChannel.MapMode.READ_WRITE,
                    0,
                    size,
                    arena
                );

                // Write header
                mapped.set(ValueLayout.JAVA_INT, 0, 0xABCD1234);
                mapped.set(ValueLayout.JAVA_INT, 4, 1);

                // Write data
                for (int i = 0; i < 100; i++) {
                    mapped.set(ValueLayout.JAVA_INT, 8 + (i * 4L), i);
                }

                System.out.println("Written to memory-mapped file");
            }
        }
    }
}

Real-World Example: Binary Protocol Parser

import java.lang.foreign.*;
import java.nio.ByteOrder;
import java.util.*;

public class ProtocolParser {
    // Message structure:
    // - Header (16 bytes):
    //   - Magic (4 bytes): 0x50524F54 ("PROT")
    //   - Version (2 bytes)
    //   - Type (2 bytes)
    //   - Length (4 bytes)
    //   - Flags (4 bytes)
    // - Payload (variable)

    private static final int MAGIC = 0x50524F54;
    private static final int HEADER_SIZE = 16;

    private static final MemoryLayout HEADER_LAYOUT = MemoryLayout.structLayout(
        ValueLayout.JAVA_INT.withName("magic"),
        ValueLayout.JAVA_SHORT.withName("version"),
        ValueLayout.JAVA_SHORT.withName("type"),
        ValueLayout.JAVA_INT.withName("length"),
        ValueLayout.JAVA_INT.withName("flags")
    );

    public static class Message {
        public final short version;
        public final short type;
        public final int flags;
        public final byte[] payload;

        public Message(short version, short type, int flags, byte[] payload) {
            this.version = version;
            this.type = type;
            this.flags = flags;
            this.payload = payload;
        }

        @Override
        public String toString() {
            return String.format(
                "Message[version=%d, type=%d, flags=0x%X, payload=%d bytes]",
                version, type, flags, payload.length
            );
        }
    }

    /**
     * Parse binary protocol message
     */
    public static Message parse(byte[] data) {
        try (Arena arena = Arena.ofConfined()) {
            if (data.length < HEADER_SIZE) {
                throw new IllegalArgumentException("Data too short for header");
            }

            // Copy to native memory
            MemorySegment segment = arena.allocateArray(ValueLayout.JAVA_BYTE, data);

            // Parse header
            int magic = segment.get(ValueLayout.JAVA_INT, 0);
            if (magic != MAGIC) {
                throw new IllegalArgumentException(
                    String.format("Invalid magic: 0x%X", magic)
                );
            }

            short version = segment.get(ValueLayout.JAVA_SHORT, 4);
            short type = segment.get(ValueLayout.JAVA_SHORT, 6);
            int length = segment.get(ValueLayout.JAVA_INT, 8);
            int flags = segment.get(ValueLayout.JAVA_INT, 12);

            if (data.length < HEADER_SIZE + length) {
                throw new IllegalArgumentException("Incomplete message");
            }

            // Extract payload
            byte[] payload = new byte[length];
            MemorySegment.copy(
                segment, ValueLayout.JAVA_BYTE, HEADER_SIZE,
                payload, 0,
                length
            );

            return new Message(version, type, flags, payload);
        }
    }

    /**
     * Serialize message to binary
     */
    public static byte[] serialize(Message message) {
        try (Arena arena = Arena.ofConfined()) {
            int totalSize = HEADER_SIZE + message.payload.length;
            MemorySegment segment = arena.allocate(totalSize);

            // Write header
            segment.set(ValueLayout.JAVA_INT, 0, MAGIC);
            segment.set(ValueLayout.JAVA_SHORT, 4, message.version);
            segment.set(ValueLayout.JAVA_SHORT, 6, message.type);
            segment.set(ValueLayout.JAVA_INT, 8, message.payload.length);
            segment.set(ValueLayout.JAVA_INT, 12, message.flags);

            // Write payload
            MemorySegment.copy(
                message.payload, 0,
                segment, ValueLayout.JAVA_BYTE, HEADER_SIZE,
                message.payload.length
            );

            // Convert to byte array
            return segment.toArray(ValueLayout.JAVA_BYTE);
        }
    }

    /**
     * Parse multiple messages from stream
     */
    public static List<Message> parseStream(byte[] data) {
        List<Message> messages = new ArrayList<>();
        int offset = 0;

        while (offset < data.length) {
            if (data.length - offset < HEADER_SIZE) {
                break;  // Incomplete header
            }

            // Read length to determine message boundary
            try (Arena arena = Arena.ofConfined()) {
                MemorySegment headerSeg = arena.allocateArray(
                    ValueLayout.JAVA_BYTE,
                    data,
                    offset,
                    HEADER_SIZE
                );

                int length = headerSeg.get(ValueLayout.JAVA_INT, 8);
                int messageSize = HEADER_SIZE + length;

                if (offset + messageSize > data.length) {
                    break;  // Incomplete message
                }

                // Extract and parse complete message
                byte[] messageData = Arrays.copyOfRange(
                    data,
                    offset,
                    offset + messageSize
                );

                messages.add(parse(messageData));
                offset += messageSize;
            }
        }

        return messages;
    }

    // Usage example
    public static void main(String[] args) {
        // Create message
        Message msg = new Message(
            (short) 1,
            (short) 100,
            0x0001,
            "Hello, Protocol!".getBytes()
        );

        // Serialize
        byte[] data = serialize(msg);
        System.out.println("Serialized: " + data.length + " bytes");

        // Parse back
        Message parsed = parse(data);
        System.out.println("Parsed: " + parsed);
        System.out.println("Payload: " + new String(parsed.payload));
    }
}

Best Practices

1. Use Appropriate Access Methods:

// Good - typed access
segment.get(ValueLayout.JAVA_INT, offset);

// Bad - manual byte manipulation
int value = (segment.get(ValueLayout.JAVA_BYTE, offset) & 0xFF) |
            ((segment.get(ValueLayout.JAVA_BYTE, offset + 1) & 0xFF) << 8) |
            ((segment.get(ValueLayout.JAVA_BYTE, offset + 2) & 0xFF) << 16) |
            ((segment.get(ValueLayout.JAVA_BYTE, offset + 3) & 0xFF) << 24);

2. Check Bounds:

// Good - validate before access
if (offset + ValueLayout.JAVA_INT.byteSize() <= segment.byteSize()) {
    int value = segment.get(ValueLayout.JAVA_INT, offset);
}

// Bad - can throw IndexOutOfBoundsException
int value = segment.get(ValueLayout.JAVA_INT, offset);

3. Use Bulk Operations:

// Good - efficient bulk copy
MemorySegment.copy(source, 0, dest, 0, 1024);

// Bad - byte-by-byte copy
for (int i = 0; i < 1024; i++) {
    dest.set(ValueLayout.JAVA_BYTE, i, source.get(ValueLayout.JAVA_BYTE, i));
}

4. Leverage Slicing:

// Good - create views
MemorySegment header = buffer.asSlice(0, 64);
MemorySegment data = buffer.asSlice(64, buffer.byteSize() - 64);

// Bad - manual offset tracking
int headerOffset = 0;
int dataOffset = 64;

5. Handle Endianness:

// Explicit endianness when needed
ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN);

These patterns enable efficient, safe memory operations for high-performance applications.