Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ Package.resolved
*/**/*.swiftdeps~
*/**/.docc-build/


BuildLogic/.kotlin/
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

public func echoData(_ data: Data) -> Data {
return data
}

public func makeData() -> Data {
return Data([0x01, 0x02, 0x03, 0x04])
}

public func getDataCount(_ data: Data) -> Int {
return data.count
}

public func compareData(_ data1: Data, _ data2: Data) -> Bool {
return data1 == data2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

package org.swift.swiftkit.ffm;

import com.example.swift.Data;
import com.example.swift.MySwiftLibrary;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Fork(value = 2, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" })
public class FFMDataBenchmark {

private static class Holder<T> {
T value;
}

@Param({"4", "100", "1000"})
public int dataSize;

ClosableAllocatingSwiftArena arena;
Data data;

@Setup(Level.Trial)
public void beforeAll() {
arena = AllocatingSwiftArena.ofConfined();
data = Data.init(makeBytes(dataSize), arena);
}

@TearDown(Level.Trial)
public void afterAll() {
arena.close();
}

private static byte[] makeBytes(int size) {
byte[] bytes = new byte[size];
for (int i = 0; i < size; i++) {
bytes[i] = (byte) (i % 256);
}
return bytes;
}

@Benchmark
public long ffm_baseline_globalMakeInt() {
return MySwiftLibrary.globalMakeInt();
}

@Benchmark
public long ffm_passDataToSwift() {
return MySwiftLibrary.getDataCount(data);
}

@Benchmark
public ByteBuffer ffm_data_withUnsafeBytes_asByteBuffer() {
Holder<ByteBuffer> buf = new Holder<>();
data.withUnsafeBytes((bytes) -> {
buf.value = bytes.asByteBuffer();
});
return buf.value;
}

@Benchmark
public byte[] ffm_data_withUnsafeBytes_toArray() {
Holder<byte[]> buf = new Holder<>();
data.withUnsafeBytes((bytes) -> {
buf.value = bytes.toArray(ValueLayout.JAVA_BYTE);
});
return buf.value;
}

@Benchmark
public byte[] ffm_data_toByteArray() {
return data.toByteArray();
}

@Benchmark
public byte[] ffm_data_toByteArray_withArena() {
return data.toByteArray(arena);
}

@Benchmark
public MemorySegment ffm_data_toMemorySegment() {
return data.toMemorySegment(arena);
}

@Benchmark
public ByteBuffer ffm_data_toByteBuffer() {
return data.toByteBuffer(arena);
}

@Benchmark
public Data ffm_receiveDataFromSwift(Blackhole bh) {
Data result = MySwiftLibrary.makeData(arena);
bh.consume(result.getCount());
return result;
}

@Benchmark
public Data ffm_echoData(Blackhole bh) {
Data echoed = MySwiftLibrary.echoData(data, arena);
bh.consume(echoed.getCount());
return echoed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import org.junit.jupiter.api.Test;
import org.swift.swiftkit.ffm.AllocatingSwiftArena;

import java.lang.foreign.ValueLayout;

import static org.junit.jupiter.api.Assertions.*;

public class DataImportTest {
Expand Down Expand Up @@ -46,4 +48,95 @@ void test_DataProtocol_receive() {
assertEquals(6, result);
}
}

@Test
void test_Data_toByteArray() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
byte[] original = new byte[] { 10, 20, 30, 40 };
var data = Data.fromByteArray(original, arena);
byte[] result = data.toByteArray();
assertEquals(original.length, result.length);
assertArrayEquals(original, result);
}
}

@Test
void test_Data_toByteArray_withArena() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
byte[] original = new byte[] { 10, 20, 30, 40 };
var data = Data.fromByteArray(original, arena);
byte[] result = data.toByteArray(arena);
assertEquals(original.length, result.length);
assertArrayEquals(original, result);
}
}

@Test
void test_Data_toByteArray_emptyData() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
byte[] original = new byte[0];
var data = Data.fromByteArray(original, arena);
byte[] result = data.toByteArray();
assertEquals(0, result.length);
}
}

@Test
void test_Data_fromByteArray() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
byte[] original = new byte[] { 1, 2, 3, 4, 5 };
var data = Data.fromByteArray(original, arena);
assertEquals(5, data.getCount());
}
}

@Test
void test_Data_toMemorySegment() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
byte[] original = new byte[] { 10, 20, 30, 40 };
var data = Data.fromByteArray(original, arena);
var segment = data.toMemorySegment(arena);

assertEquals(original.length, segment.byteSize());
for (int i = 0; i < original.length; i++) {
assertEquals(original[i], segment.get(ValueLayout.JAVA_BYTE, i));
}
}
}

@Test
void test_Data_toByteBuffer() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
byte[] original = new byte[] { 10, 20, 30, 40 };
var data = Data.fromByteArray(original, arena);
var buffer = data.toByteBuffer(arena);

assertEquals(original.length, buffer.capacity());
for (int i = 0; i < original.length; i++) {
assertEquals(original[i], buffer.get(i));
}
}
}

@Test
void test_Data_toMemorySegment_emptyData() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
byte[] original = new byte[0];
var data = Data.fromByteArray(original, arena);
var segment = data.toMemorySegment(arena);
assertEquals(0, segment.byteSize());
}
}

@Test
void test_Data_toByteBuffer_emptyData() {
try (var arena = AllocatingSwiftArena.ofConfined()) {
byte[] original = new byte[0];
var data = Data.fromByteArray(original, arena);
var buffer = data.toByteBuffer(arena);

assertEquals(0, buffer.capacity());
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif
import SwiftJava

public func echoData(_ data: Data) -> Data {
return data
}

public func makeData() -> Data {
return Data([0x01, 0x02, 0x03, 0x04])
}

public func getDataCount(_ data: Data) -> Int {
return data.count
}

public func compareData(_ data1: Data, _ data2: Data) -> Bool {
return data1 == data2
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public func globalTakeInt(i: Int64) {
p("i:\(i)")
}

public func globalEchoInt(i: Int64) -> Int64{
i
}

public func globalMakeInt() -> Int64 {
return 42
}
Expand Down
Loading
Loading