This is an automated email from the ASF dual-hosted git repository. raulcd pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/arrow-swift.git
commit df4e1bfb13af9c43386e7fcb10272d96fc8b423e Author: abandy <[email protected]> AuthorDate: Tue May 28 22:58:22 2024 -0400 GH-37938: [Swift] Add initial C data interface implementation (#41342) Continuation for PR: #39091 This add an initial implementation of the C Data interface for swift. During development it was found that null count was not being properly maintained on the arrow buffers and this change is included as well. Also some minor refactoring was done to existing sources to enable this feature. This has been tested from Swift calling into C to import data but not from Swift to C exporting data. Test is currently ongoing. * GitHub Issue: #37938 Authored-by: Alva Bandy <[email protected]> Signed-off-by: Sutou Kouhei <[email protected]> --- .swiftlint.yml | 4 + Arrow/Package.swift | 22 ++- Arrow/Sources/Arrow/ArrowArray.swift | 39 +++-- Arrow/Sources/Arrow/ArrowBuffer.swift | 17 +- Arrow/Sources/Arrow/ArrowCExporter.swift | 135 ++++++++++++++++ Arrow/Sources/Arrow/ArrowCImporter.swift | 179 +++++++++++++++++++++ Arrow/Sources/Arrow/ArrowReaderHelper.swift | 16 +- Arrow/Sources/Arrow/ArrowSchema.swift | 6 +- Arrow/Sources/Arrow/ArrowTable.swift | 18 ++- Arrow/Sources/Arrow/ArrowType.swift | 116 +++++++++++++ Arrow/Sources/Arrow/ChunkedArray.swift | 5 + Arrow/Sources/ArrowC/ArrowCData.c | 30 ++++ Arrow/Sources/ArrowC/include/ArrowCData.h | 81 ++++++++++ Arrow/Tests/ArrowTests/CDataTests.swift | 125 ++++++++++++++ Arrow/Tests/ArrowTests/IPCTests.swift | 16 +- Arrow/Tests/ArrowTests/RecordBatchTests.swift | 4 +- Arrow/Tests/ArrowTests/TableTests.swift | 4 +- ArrowFlight/Package.swift | 4 +- .../Tests/ArrowFlightTests/FlightTest.swift | 6 +- CDataWGo/.gitignore | 8 + CDataWGo/Package.swift | 43 +++++ CDataWGo/Sources/go-swift/CDataTest.swift | 132 +++++++++++++++ CDataWGo/go.mod | 41 +++++ CDataWGo/go.sum | 75 +++++++++ CDataWGo/include/go_swift.h | 30 ++++ CDataWGo/main.go | 127 +++++++++++++++ 26 files changed, 1229 insertions(+), 54 deletions(-) diff --git a/.swiftlint.yml b/.swiftlint.yml index d447bf9..7e4da29 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -16,10 +16,14 @@ # under the License. included: + - Arrow/Package.swift - Arrow/Sources - Arrow/Tests + - ArrowFlight/Package.swift - ArrowFlight/Sources - ArrowFlight/Tests + - CDataWGo/Package.swift + - CDataWGo/Sources/go-swift excluded: - Arrow/Sources/Arrow/File_generated.swift - Arrow/Sources/Arrow/Message_generated.swift diff --git a/Arrow/Package.swift b/Arrow/Package.swift index 946eb99..6f19136 100644 --- a/Arrow/Package.swift +++ b/Arrow/Package.swift @@ -26,28 +26,34 @@ let package = Package( .macOS(.v10_14) ], products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "Arrow", - targets: ["Arrow"]), + targets: ["Arrow"]) ], dependencies: [ // The latest version of flatbuffers v23.5.26 was built in May 26, 2023 // and therefore doesn't include the unaligned buffer swift changes. // This can be changed back to using the tag once a new version of // flatbuffers has been released. - .package(url: "https://github.com/google/flatbuffers.git", branch: "master") + .package(url: "https://github.com/google/flatbuffers.git", branch: "master"), + .package( + url: "https://github.com/apple/swift-atomics.git", + .upToNextMajor(from: "1.2.0") // or `.upToNextMinor + ) ], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "ArrowC", + path: "Sources/ArrowC" + ), .target( name: "Arrow", - dependencies: [ - .product(name: "FlatBuffers", package: "flatbuffers") + dependencies: ["ArrowC", + .product(name: "FlatBuffers", package: "flatbuffers"), + .product(name: "Atomics", package: "swift-atomics") ]), .testTarget( name: "ArrowTests", - dependencies: ["Arrow"]), + dependencies: ["Arrow", "ArrowC"]) ] ) diff --git a/Arrow/Sources/Arrow/ArrowArray.swift b/Arrow/Sources/Arrow/ArrowArray.swift index 88b43e6..32b6ba1 100644 --- a/Arrow/Sources/Arrow/ArrowArray.swift +++ b/Arrow/Sources/Arrow/ArrowArray.swift @@ -17,16 +17,29 @@ import Foundation -public class ArrowArrayHolder { +public protocol ArrowArrayHolder { + var type: ArrowType {get} + var length: UInt {get} + var nullCount: UInt {get} + var array: Any {get} + var data: ArrowData {get} + var getBufferData: () -> [Data] {get} + var getBufferDataSizes: () -> [Int] {get} + var getArrowColumn: (ArrowField, [ArrowArrayHolder]) throws -> ArrowColumn {get} +} + +public class ArrowArrayHolderImpl: ArrowArrayHolder { + public let array: Any + public let data: ArrowData public let type: ArrowType public let length: UInt public let nullCount: UInt - public let array: Any public let getBufferData: () -> [Data] public let getBufferDataSizes: () -> [Int] - private let getArrowColumn: (ArrowField, [ArrowArrayHolder]) throws -> ArrowColumn + public let getArrowColumn: (ArrowField, [ArrowArrayHolder]) throws -> ArrowColumn public init<T>(_ arrowArray: ArrowArray<T>) { self.array = arrowArray + self.data = arrowArray.arrowData self.length = arrowArray.length self.type = arrowArray.arrowData.type self.nullCount = arrowArray.nullCount @@ -60,19 +73,9 @@ public class ArrowArrayHolder { return ArrowColumn(field, chunked: ChunkedArrayHolder(try ChunkedArray<T>(arrays))) } } - - public static func makeArrowColumn(_ field: ArrowField, - holders: [ArrowArrayHolder] - ) -> Result<ArrowColumn, ArrowError> { - do { - return .success(try holders[0].getArrowColumn(field, holders)) - } catch { - return .failure(.runtimeError("\(error)")) - } - } } -public class ArrowArray<T>: AsString { +public class ArrowArray<T>: AsString, AnyArray { public typealias ItemType = T public let arrowData: ArrowData public var nullCount: UInt {return self.arrowData.nullCount} @@ -101,6 +104,14 @@ public class ArrowArray<T>: AsString { return "\(self[index]!)" } + + public func asAny(_ index: UInt) -> Any? { + if self[index] == nil { + return nil + } + + return self[index]! + } } public class FixedArray<T>: ArrowArray<T> { diff --git a/Arrow/Sources/Arrow/ArrowBuffer.swift b/Arrow/Sources/Arrow/ArrowBuffer.swift index 4ac4eb9..1ff53cd 100644 --- a/Arrow/Sources/Arrow/ArrowBuffer.swift +++ b/Arrow/Sources/Arrow/ArrowBuffer.swift @@ -22,16 +22,20 @@ public class ArrowBuffer { static let maxLength = UInt.max fileprivate(set) var length: UInt let capacity: UInt - let rawPointer: UnsafeMutableRawPointer + public let rawPointer: UnsafeMutableRawPointer + let isMemoryOwner: Bool - init(length: UInt, capacity: UInt, rawPointer: UnsafeMutableRawPointer) { + init(length: UInt, capacity: UInt, rawPointer: UnsafeMutableRawPointer, isMemoryOwner: Bool = true) { self.length = length self.capacity = capacity self.rawPointer = rawPointer + self.isMemoryOwner = isMemoryOwner } deinit { - self.rawPointer.deallocate() + if isMemoryOwner { + self.rawPointer.deallocate() + } } func append(to data: inout Data) { @@ -39,6 +43,13 @@ public class ArrowBuffer { data.append(ptr, count: Int(capacity)) } + static func createEmptyBuffer() -> ArrowBuffer { + return ArrowBuffer( + length: 0, + capacity: 0, + rawPointer: UnsafeMutableRawPointer.allocate(byteCount: 0, alignment: .zero)) + } + static func createBuffer(_ data: [UInt8], length: UInt) -> ArrowBuffer { let byteCount = UInt(data.count) let capacity = alignTo64(byteCount) diff --git a/Arrow/Sources/Arrow/ArrowCExporter.swift b/Arrow/Sources/Arrow/ArrowCExporter.swift new file mode 100644 index 0000000..aa93f0c --- /dev/null +++ b/Arrow/Sources/Arrow/ArrowCExporter.swift @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import Foundation +import ArrowC +import Atomics + +// The memory used by UnsafeAtomic is not automatically +// reclaimed. Since this value is initialized once +// and used until the program/app is closed it's +// memory will be released on program/app exit +let exportDataCounter: UnsafeAtomic<Int> = .create(0) + +public class ArrowCExporter { + private class ExportData { + let id: Int + init() { + id = exportDataCounter.loadThenWrappingIncrement(ordering: .relaxed) + ArrowCExporter.exportedData[id] = self + } + } + + private class ExportSchema: ExportData { + public let arrowTypeNameCstr: UnsafePointer<CChar> + public let nameCstr: UnsafePointer<CChar> + private let arrowType: ArrowType + private let name: String + private let arrowTypeName: String + init(_ arrowType: ArrowType, name: String = "") throws { + self.arrowType = arrowType + // keeping the name str to ensure the cstring buffer remains valid + self.name = name + self.arrowTypeName = try arrowType.cDataFormatId + self.nameCstr = (self.name as NSString).utf8String! + self.arrowTypeNameCstr = (self.arrowTypeName as NSString).utf8String! + super.init() + } + } + + private class ExportArray: ExportData { + private let arrowData: ArrowData + private(set) var data = [UnsafeRawPointer?]() + private(set) var buffers: UnsafeMutablePointer<UnsafeRawPointer?> + init(_ arrowData: ArrowData) { + // keep a reference to the ArrowData + // obj so the memory doesn't get + // deallocated + self.arrowData = arrowData + for arrowBuffer in arrowData.buffers { + data.append(arrowBuffer.rawPointer) + } + + self.buffers = UnsafeMutablePointer(mutating: data) + super.init() + } + } + + private static var exportedData = [Int: ExportData]() + public init() {} + + public func exportType(_ cSchema: inout ArrowC.ArrowSchema, arrowType: ArrowType, name: String = "") -> + Result<Bool, ArrowError> { + do { + let exportSchema = try ExportSchema(arrowType, name: name) + cSchema.format = exportSchema.arrowTypeNameCstr + cSchema.name = exportSchema.nameCstr + cSchema.private_data = + UnsafeMutableRawPointer(mutating: UnsafeRawPointer(bitPattern: exportSchema.id)) + cSchema.release = {(data: UnsafeMutablePointer<ArrowC.ArrowSchema>?) in + let arraySchema = data!.pointee + let exportId = Int(bitPattern: arraySchema.private_data) + guard ArrowCExporter.exportedData[exportId] != nil else { + fatalError("Export schema not found with id \(exportId)") + } + + // the data associated with this exportSchema object + // which includes the C strings for the format and name + // be deallocated upon removal + ArrowCExporter.exportedData.removeValue(forKey: exportId) + ArrowC.ArrowSwiftClearReleaseSchema(data) + } + } catch { + return .failure(.unknownError("\(error)")) + } + return .success(true) + } + + public func exportField(_ schema: inout ArrowC.ArrowSchema, field: ArrowField) -> + Result<Bool, ArrowError> { + return exportType(&schema, arrowType: field.type, name: field.name) + } + + public func exportArray(_ cArray: inout ArrowC.ArrowArray, arrowData: ArrowData) { + let exportArray = ExportArray(arrowData) + cArray.buffers = exportArray.buffers + cArray.length = Int64(arrowData.length) + cArray.null_count = Int64(arrowData.nullCount) + cArray.n_buffers = Int64(arrowData.buffers.count) + // Swift Arrow does not currently support children or dictionaries + // This will need to be updated once support has been added + cArray.n_children = 0 + cArray.children = nil + cArray.dictionary = nil + cArray.private_data = + UnsafeMutableRawPointer(mutating: UnsafeRawPointer(bitPattern: exportArray.id)) + cArray.release = {(data: UnsafeMutablePointer<ArrowC.ArrowArray>?) in + let arrayData = data!.pointee + let exportId = Int(bitPattern: arrayData.private_data) + guard ArrowCExporter.exportedData[exportId] != nil else { + fatalError("Export data not found with id \(exportId)") + } + + // the data associated with this exportArray object + // which includes the entire arrowData object + // and the buffers UnsafeMutablePointer[] will + // be deallocated upon removal + ArrowCExporter.exportedData.removeValue(forKey: exportId) + ArrowC.ArrowSwiftClearReleaseArray(data) + } + } +} diff --git a/Arrow/Sources/Arrow/ArrowCImporter.swift b/Arrow/Sources/Arrow/ArrowCImporter.swift new file mode 100644 index 0000000..8a4cf47 --- /dev/null +++ b/Arrow/Sources/Arrow/ArrowCImporter.swift @@ -0,0 +1,179 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import Foundation +import ArrowC + +public class ImportArrayHolder: ArrowArrayHolder { + let cArrayPtr: UnsafePointer<ArrowC.ArrowArray> + public var type: ArrowType {self.holder.type} + public var length: UInt {self.holder.length} + public var nullCount: UInt {self.holder.nullCount} + public var array: Any {self.holder.array} + public var data: ArrowData {self.holder.data} + public var getBufferData: () -> [Data] {self.holder.getBufferData} + public var getBufferDataSizes: () -> [Int] {self.holder.getBufferDataSizes} + public var getArrowColumn: (ArrowField, [ArrowArrayHolder]) throws -> ArrowColumn {self.holder.getArrowColumn} + private let holder: ArrowArrayHolder + init(_ holder: ArrowArrayHolder, cArrayPtr: UnsafePointer<ArrowC.ArrowArray>) { + self.cArrayPtr = cArrayPtr + self.holder = holder + } + + deinit { + if self.cArrayPtr.pointee.release != nil { + ArrowCImporter.release(self.cArrayPtr) + } + } +} + +public class ArrowCImporter { + private func appendToBuffer( + _ cBuffer: UnsafeRawPointer?, + arrowBuffers: inout [ArrowBuffer], + length: UInt) { + if cBuffer == nil { + arrowBuffers.append(ArrowBuffer.createEmptyBuffer()) + return + } + + let pointer = UnsafeMutableRawPointer(mutating: cBuffer)! + arrowBuffers.append( + ArrowBuffer(length: length, capacity: length, rawPointer: pointer, isMemoryOwner: false)) + } + + public init() {} + + public func importType(_ cArrow: String, name: String = "") -> + Result<ArrowField, ArrowError> { + do { + let type = try ArrowType.fromCDataFormatId(cArrow) + return .success(ArrowField(name, type: ArrowType(type.info), isNullable: true)) + } catch { + return .failure(.invalid("Error occurred while attempting to import type: \(error)")) + } + } + + public func importField(_ cSchema: ArrowC.ArrowSchema) -> + Result<ArrowField, ArrowError> { + if cSchema.n_children > 0 { + ArrowCImporter.release(cSchema) + return .failure(.invalid("Children currently not supported")) + } else if cSchema.dictionary != nil { + ArrowCImporter.release(cSchema) + return .failure(.invalid("Dictinoary types currently not supported")) + } + + switch importType( + String(cString: cSchema.format), name: String(cString: cSchema.name)) { + case .success(let field): + ArrowCImporter.release(cSchema) + return .success(field) + case .failure(let err): + ArrowCImporter.release(cSchema) + return .failure(err) + } + } + + public func importArray( + _ cArray: UnsafePointer<ArrowC.ArrowArray>, + arrowType: ArrowType, + isNullable: Bool = false + ) -> Result<ArrowArrayHolder, ArrowError> { + let arrowField = ArrowField("", type: arrowType, isNullable: isNullable) + return importArray(cArray, arrowField: arrowField) + } + + public func importArray( // swiftlint:disable:this cyclomatic_complexity function_body_length + _ cArrayPtr: UnsafePointer<ArrowC.ArrowArray>, + arrowField: ArrowField + ) -> Result<ArrowArrayHolder, ArrowError> { + let cArray = cArrayPtr.pointee + if cArray.null_count < 0 { + ArrowCImporter.release(cArrayPtr) + return .failure(.invalid("Uncomputed null count is not supported")) + } else if cArray.n_children > 0 { + ArrowCImporter.release(cArrayPtr) + return .failure(.invalid("Children currently not supported")) + } else if cArray.dictionary != nil { + ArrowCImporter.release(cArrayPtr) + return .failure(.invalid("Dictionary types currently not supported")) + } else if cArray.offset != 0 { + ArrowCImporter.release(cArrayPtr) + return .failure(.invalid("Offset of 0 is required but found offset: \(cArray.offset)")) + } + + let arrowType = arrowField.type + let length = UInt(cArray.length) + let nullCount = UInt(cArray.null_count) + var arrowBuffers = [ArrowBuffer]() + + if cArray.n_buffers > 0 { + if cArray.buffers == nil { + ArrowCImporter.release(cArrayPtr) + return .failure(.invalid("C array buffers is nil")) + } + + switch arrowType.info { + case .variableInfo: + if cArray.n_buffers != 3 { + ArrowCImporter.release(cArrayPtr) + return .failure( + .invalid("Variable buffer count expected 3 but found \(cArray.n_buffers)")) + } + + appendToBuffer(cArray.buffers[0], arrowBuffers: &arrowBuffers, length: UInt(ceil(Double(length) / 8))) + appendToBuffer(cArray.buffers[1], arrowBuffers: &arrowBuffers, length: length) + let lastOffsetLength = cArray.buffers[1]! + .advanced(by: Int(length) * MemoryLayout<Int32>.stride) + .load(as: Int32.self) + appendToBuffer(cArray.buffers[2], arrowBuffers: &arrowBuffers, length: UInt(lastOffsetLength)) + default: + if cArray.n_buffers != 2 { + ArrowCImporter.release(cArrayPtr) + return .failure(.invalid("Expected buffer count 2 but found \(cArray.n_buffers)")) + } + + appendToBuffer(cArray.buffers[0], arrowBuffers: &arrowBuffers, length: UInt(ceil(Double(length) / 8))) + appendToBuffer(cArray.buffers[1], arrowBuffers: &arrowBuffers, length: length) + } + } + + switch makeArrayHolder(arrowField, buffers: arrowBuffers, nullCount: nullCount) { + case .success(let holder): + return .success(ImportArrayHolder(holder, cArrayPtr: cArrayPtr)) + case .failure(let err): + ArrowCImporter.release(cArrayPtr) + return .failure(err) + } + } + + public static func release(_ cArrayPtr: UnsafePointer<ArrowC.ArrowArray>) { + if cArrayPtr.pointee.release != nil { + let cSchemaMutablePtr = UnsafeMutablePointer<ArrowC.ArrowArray>(mutating: cArrayPtr) + cArrayPtr.pointee.release(cSchemaMutablePtr) + } + } + + public static func release(_ cSchema: ArrowC.ArrowSchema) { + if cSchema.release != nil { + let cSchemaPtr = UnsafeMutablePointer<ArrowC.ArrowSchema>.allocate(capacity: 1) + cSchemaPtr.initialize(to: cSchema) + cSchema.release(cSchemaPtr) + } + } +} diff --git a/Arrow/Sources/Arrow/ArrowReaderHelper.swift b/Arrow/Sources/Arrow/ArrowReaderHelper.swift index fb4a13b..c701653 100644 --- a/Arrow/Sources/Arrow/ArrowReaderHelper.swift +++ b/Arrow/Sources/Arrow/ArrowReaderHelper.swift @@ -23,7 +23,7 @@ private func makeBinaryHolder(_ buffers: [ArrowBuffer], do { let arrowType = ArrowType(ArrowType.ArrowBinary) let arrowData = try ArrowData(arrowType, buffers: buffers, nullCount: nullCount) - return .success(ArrowArrayHolder(BinaryArray(arrowData))) + return .success(ArrowArrayHolderImpl(BinaryArray(arrowData))) } catch let error as ArrowError { return .failure(error) } catch { @@ -36,7 +36,7 @@ private func makeStringHolder(_ buffers: [ArrowBuffer], do { let arrowType = ArrowType(ArrowType.ArrowString) let arrowData = try ArrowData(arrowType, buffers: buffers, nullCount: nullCount) - return .success(ArrowArrayHolder(StringArray(arrowData))) + return .success(ArrowArrayHolderImpl(StringArray(arrowData))) } catch let error as ArrowError { return .failure(error) } catch { @@ -51,11 +51,11 @@ private func makeDateHolder(_ field: ArrowField, do { if field.type.id == .date32 { let arrowData = try ArrowData(field.type, buffers: buffers, nullCount: nullCount) - return .success(ArrowArrayHolder(Date32Array(arrowData))) + return .success(ArrowArrayHolderImpl(Date32Array(arrowData))) } let arrowData = try ArrowData(field.type, buffers: buffers, nullCount: nullCount) - return .success(ArrowArrayHolder(Date64Array(arrowData))) + return .success(ArrowArrayHolderImpl(Date64Array(arrowData))) } catch let error as ArrowError { return .failure(error) } catch { @@ -71,7 +71,7 @@ private func makeTimeHolder(_ field: ArrowField, if field.type.id == .time32 { if let arrowType = field.type as? ArrowTypeTime32 { let arrowData = try ArrowData(arrowType, buffers: buffers, nullCount: nullCount) - return .success(ArrowArrayHolder(FixedArray<Time32>(arrowData))) + return .success(ArrowArrayHolderImpl(FixedArray<Time32>(arrowData))) } else { return .failure(.invalid("Incorrect field type for time: \(field.type)")) } @@ -79,7 +79,7 @@ private func makeTimeHolder(_ field: ArrowField, if let arrowType = field.type as? ArrowTypeTime64 { let arrowData = try ArrowData(arrowType, buffers: buffers, nullCount: nullCount) - return .success(ArrowArrayHolder(FixedArray<Time64>(arrowData))) + return .success(ArrowArrayHolderImpl(FixedArray<Time64>(arrowData))) } else { return .failure(.invalid("Incorrect field type for time: \(field.type)")) } @@ -95,7 +95,7 @@ private func makeBoolHolder(_ buffers: [ArrowBuffer], do { let arrowType = ArrowType(ArrowType.ArrowBool) let arrowData = try ArrowData(arrowType, buffers: buffers, nullCount: nullCount) - return .success(ArrowArrayHolder(BoolArray(arrowData))) + return .success(ArrowArrayHolderImpl(BoolArray(arrowData))) } catch let error as ArrowError { return .failure(error) } catch { @@ -109,7 +109,7 @@ private func makeFixedHolder<T>( ) -> Result<ArrowArrayHolder, ArrowError> { do { let arrowData = try ArrowData(field.type, buffers: buffers, nullCount: nullCount) - return .success(ArrowArrayHolder(FixedArray<T>(arrowData))) + return .success(ArrowArrayHolderImpl(FixedArray<T>(arrowData))) } catch let error as ArrowError { return .failure(error) } catch { diff --git a/Arrow/Sources/Arrow/ArrowSchema.swift b/Arrow/Sources/Arrow/ArrowSchema.swift index 45f13a1..65c506d 100644 --- a/Arrow/Sources/Arrow/ArrowSchema.swift +++ b/Arrow/Sources/Arrow/ArrowSchema.swift @@ -17,9 +17,9 @@ import Foundation public class ArrowField { - let type: ArrowType - let name: String - let isNullable: Bool + public let type: ArrowType + public let name: String + public let isNullable: Bool init(_ name: String, type: ArrowType, isNullable: Bool) { self.name = name diff --git a/Arrow/Sources/Arrow/ArrowTable.swift b/Arrow/Sources/Arrow/ArrowTable.swift index 7677fb4..b9d1515 100644 --- a/Arrow/Sources/Arrow/ArrowTable.swift +++ b/Arrow/Sources/Arrow/ArrowTable.swift @@ -64,7 +64,7 @@ public class ArrowTable { let builder = ArrowTable.Builder() for index in 0..<schema.fields.count { - switch ArrowArrayHolder.makeArrowColumn(schema.fields[index], holders: holders[index]) { + switch makeArrowColumn(schema.fields[index], holders: holders[index]) { case .success(let column): builder.addColumn(column) case .failure(let error): @@ -75,6 +75,17 @@ public class ArrowTable { return .success(builder.finish()) } + private static func makeArrowColumn( + _ field: ArrowField, + holders: [ArrowArrayHolder] + ) -> Result<ArrowColumn, ArrowError> { + do { + return .success(try holders[0].getArrowColumn(field, holders)) + } catch { + return .failure(.runtimeError("\(error)")) + } + } + public class Builder { let schemaBuilder = ArrowSchema.Builder() var columns = [ArrowColumn]() @@ -172,6 +183,11 @@ public class RecordBatch { return (arrayHolder.array as! ArrowArray<T>) // swiftlint:disable:this force_cast } + public func anyData(for columnIndex: Int) -> AnyArray { + let arrayHolder = column(columnIndex) + return (arrayHolder.array as! AnyArray) // swiftlint:disable:this force_cast + } + public func column(_ index: Int) -> ArrowArrayHolder { return self.columns[index] } diff --git a/Arrow/Sources/Arrow/ArrowType.swift b/Arrow/Sources/Arrow/ArrowType.swift index f5a869f..e1ada4b 100644 --- a/Arrow/Sources/Arrow/ArrowType.swift +++ b/Arrow/Sources/Arrow/ArrowType.swift @@ -90,6 +90,17 @@ public class ArrowTypeTime32: ArrowType { self.unit = unit super.init(ArrowType.ArrowTime32) } + + public override var cDataFormatId: String { + get throws { + switch self.unit { + case .milliseconds: + return "ttm" + case .seconds: + return "tts" + } + } + } } public class ArrowTypeTime64: ArrowType { @@ -98,6 +109,17 @@ public class ArrowTypeTime64: ArrowType { self.unit = unit super.init(ArrowType.ArrowTime64) } + + public override var cDataFormatId: String { + get throws { + switch self.unit { + case .microseconds: + return "ttu" + case .nanoseconds: + return "ttn" + } + } + } } public class ArrowType { @@ -209,6 +231,100 @@ public class ArrowType { fatalError("Stride requested for unknown type: \(self)") } } + + public var cDataFormatId: String { + get throws { + switch self.id { + case ArrowTypeId.int8: + return "c" + case ArrowTypeId.int16: + return "s" + case ArrowTypeId.int32: + return "i" + case ArrowTypeId.int64: + return "l" + case ArrowTypeId.uint8: + return "C" + case ArrowTypeId.uint16: + return "S" + case ArrowTypeId.uint32: + return "I" + case ArrowTypeId.uint64: + return "L" + case ArrowTypeId.float: + return "f" + case ArrowTypeId.double: + return "g" + case ArrowTypeId.boolean: + return "b" + case ArrowTypeId.date32: + return "tdD" + case ArrowTypeId.date64: + return "tdm" + case ArrowTypeId.time32: + if let time32 = self as? ArrowTypeTime32 { + return try time32.cDataFormatId + } + return "tts" + case ArrowTypeId.time64: + if let time64 = self as? ArrowTypeTime64 { + return try time64.cDataFormatId + } + return "ttu" + case ArrowTypeId.binary: + return "z" + case ArrowTypeId.string: + return "u" + default: + throw ArrowError.notImplemented + } + } + } + + public static func fromCDataFormatId( // swiftlint:disable:this cyclomatic_complexity + _ from: String) throws -> ArrowType { + if from == "c" { + return ArrowType(ArrowType.ArrowInt8) + } else if from == "s" { + return ArrowType(ArrowType.ArrowInt16) + } else if from == "i" { + return ArrowType(ArrowType.ArrowInt32) + } else if from == "l" { + return ArrowType(ArrowType.ArrowInt64) + } else if from == "C" { + return ArrowType(ArrowType.ArrowUInt8) + } else if from == "S" { + return ArrowType(ArrowType.ArrowUInt16) + } else if from == "I" { + return ArrowType(ArrowType.ArrowUInt32) + } else if from == "L" { + return ArrowType(ArrowType.ArrowUInt64) + } else if from == "f" { + return ArrowType(ArrowType.ArrowFloat) + } else if from == "g" { + return ArrowType(ArrowType.ArrowDouble) + } else if from == "b" { + return ArrowType(ArrowType.ArrowBool) + } else if from == "tdD" { + return ArrowType(ArrowType.ArrowDate32) + } else if from == "tdm" { + return ArrowType(ArrowType.ArrowDate64) + } else if from == "tts" { + return ArrowTypeTime32(.seconds) + } else if from == "ttm" { + return ArrowTypeTime32(.milliseconds) + } else if from == "ttu" { + return ArrowTypeTime64(.microseconds) + } else if from == "ttn" { + return ArrowTypeTime64(.nanoseconds) + } else if from == "z" { + return ArrowType(ArrowType.ArrowBinary) + } else if from == "u" { + return ArrowType(ArrowType.ArrowString) + } + + throw ArrowError.notImplemented + } } extension ArrowType.Info: Equatable { diff --git a/Arrow/Sources/Arrow/ChunkedArray.swift b/Arrow/Sources/Arrow/ChunkedArray.swift index 3a06aa4..c5ccfe4 100644 --- a/Arrow/Sources/Arrow/ChunkedArray.swift +++ b/Arrow/Sources/Arrow/ChunkedArray.swift @@ -17,6 +17,11 @@ import Foundation +public protocol AnyArray { + func asAny(_ index: UInt) -> Any? + var length: UInt {get} +} + public protocol AsString { func asString(_ index: UInt) -> String } diff --git a/Arrow/Sources/ArrowC/ArrowCData.c b/Arrow/Sources/ArrowC/ArrowCData.c new file mode 100644 index 0000000..ac366fe --- /dev/null +++ b/Arrow/Sources/ArrowC/ArrowCData.c @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "include/ArrowCData.h" + +void ArrowSwiftClearReleaseSchema(struct ArrowSchema* arrowSchema) { + if(arrowSchema) { + arrowSchema->release = NULL; + } +} + +void ArrowSwiftClearReleaseArray(struct ArrowArray* arrowArray) { + if(arrowArray) { + arrowArray->release = NULL; + } +} diff --git a/Arrow/Sources/ArrowC/include/ArrowCData.h b/Arrow/Sources/ArrowC/include/ArrowCData.h new file mode 100644 index 0000000..9df8992 --- /dev/null +++ b/Arrow/Sources/ArrowC/include/ArrowCData.h @@ -0,0 +1,81 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#ifndef ARROW_C_DATA_INTERFACE +#define ARROW_C_DATA_INTERFACE + +#define ARROW_FLAG_DICTIONARY_ORDERED 1 +#define ARROW_FLAG_NULLABLE 2 +#define ARROW_FLAG_MAP_KEYS_SORTED 4 + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> // Must have this! + + +#ifdef __cplusplus +extern "C" { +#endif + +struct ArrowSchema { + // Array type description + const char* format; + const char* name; + const char* metadata; + int64_t flags; + int64_t n_children; + struct ArrowSchema** children; + struct ArrowSchema* dictionary; + + // Release callback + void (*release)(struct ArrowSchema*); + // Opaque producer-specific data + void* private_data; +}; + +struct ArrowArray { + // Array data description + int64_t length; + int64_t null_count; + int64_t offset; + int64_t n_buffers; + int64_t n_children; + const void** buffers; + struct ArrowArray** children; + struct ArrowArray* dictionary; + + // Release callback + void (*release)(struct ArrowArray*); + // Opaque producer-specific data + void* private_data; +}; + +// Not able to set the release on the schema +// to NULL in Swift. nil in Swift is not +// equivalent to NULL. +void ArrowSwiftClearReleaseSchema(struct ArrowSchema*); + +// Not able to set the release on the array +// to NULL in Swift. nil in Swift is not +// equivalent to NULL. +void ArrowSwiftClearReleaseArray(struct ArrowArray*); + +#ifdef __cplusplus +} +#endif + +#endif // ARROW_C_DATA_INTERFACE diff --git a/Arrow/Tests/ArrowTests/CDataTests.swift b/Arrow/Tests/ArrowTests/CDataTests.swift new file mode 100644 index 0000000..2344b23 --- /dev/null +++ b/Arrow/Tests/ArrowTests/CDataTests.swift @@ -0,0 +1,125 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import Foundation +import XCTest +@testable import Arrow +import ArrowC + +final class CDataTests: XCTestCase { + func makeSchema() -> Arrow.ArrowSchema { + let schemaBuilder = ArrowSchema.Builder() + return schemaBuilder + .addField("colBool", type: ArrowType(ArrowType.ArrowBool), isNullable: false) + .addField("colUInt8", type: ArrowType(ArrowType.ArrowUInt8), isNullable: true) + .addField("colUInt16", type: ArrowType(ArrowType.ArrowUInt16), isNullable: true) + .addField("colUInt32", type: ArrowType(ArrowType.ArrowUInt32), isNullable: true) + .addField("colUInt64", type: ArrowType(ArrowType.ArrowUInt64), isNullable: true) + .addField("colInt8", type: ArrowType(ArrowType.ArrowInt8), isNullable: false) + .addField("colInt16", type: ArrowType(ArrowType.ArrowInt16), isNullable: false) + .addField("colInt32", type: ArrowType(ArrowType.ArrowInt32), isNullable: false) + .addField("colInt64", type: ArrowType(ArrowType.ArrowInt64), isNullable: false) + .addField("colString", type: ArrowType(ArrowType.ArrowString), isNullable: false) + .addField("colBinary", type: ArrowType(ArrowType.ArrowBinary), isNullable: false) + .addField("colDate32", type: ArrowType(ArrowType.ArrowDate32), isNullable: false) + .addField("colDate64", type: ArrowType(ArrowType.ArrowDate64), isNullable: false) + .addField("colTime32", type: ArrowType(ArrowType.ArrowTime32), isNullable: false) + .addField("colTime32s", type: ArrowTypeTime32(.seconds), isNullable: false) + .addField("colTime32m", type: ArrowTypeTime32(.milliseconds), isNullable: false) + .addField("colTime64", type: ArrowType(ArrowType.ArrowTime64), isNullable: false) + .addField("colTime64u", type: ArrowTypeTime64(.microseconds), isNullable: false) + .addField("colTime64n", type: ArrowTypeTime64(.nanoseconds), isNullable: false) + .addField("colTime64", type: ArrowType(ArrowType.ArrowTime64), isNullable: false) + .addField("colFloat", type: ArrowType(ArrowType.ArrowFloat), isNullable: false) + .addField("colDouble", type: ArrowType(ArrowType.ArrowDouble), isNullable: false) + .finish() + } + + func checkImportField(_ cSchema: ArrowC.ArrowSchema, name: String, type: ArrowType.Info) throws { + let importer = ArrowCImporter() + switch importer.importField(cSchema) { + case .success(let arrowField): + XCTAssertEqual(arrowField.type.info, type) + XCTAssertEqual(arrowField.name, name) + case .failure(let error): + throw error + } + } + + func testImportExportSchema() throws { + let schema = makeSchema() + let exporter = ArrowCExporter() + for arrowField in schema.fields { + var cSchema = ArrowC.ArrowSchema() + switch exporter.exportField(&cSchema, field: arrowField) { + case .success: + try checkImportField(cSchema, name: arrowField.name, type: arrowField.type.info) + case .failure(let error): + throw error + } + } + } + + func testImportExportArray() throws { + let stringBuilder = try ArrowArrayBuilders.loadStringArrayBuilder() + for index in 0..<100 { + if index % 10 == 9 { + stringBuilder.append(nil) + } else { + stringBuilder.append("test" + String(index)) + } + } + + XCTAssertEqual(stringBuilder.nullCount, 10) + XCTAssertEqual(stringBuilder.length, 100) + XCTAssertEqual(stringBuilder.capacity, 648) + let stringArray = try stringBuilder.finish() + let exporter = ArrowCExporter() + var cArray = ArrowC.ArrowArray() + exporter.exportArray(&cArray, arrowData: stringArray.arrowData) + let cArrayMutPtr = UnsafeMutablePointer<ArrowC.ArrowArray>.allocate(capacity: 1) + cArrayMutPtr.pointee = cArray + defer { + cArrayMutPtr.deallocate() + } + + let importer = ArrowCImporter() + switch importer.importArray(UnsafePointer(cArrayMutPtr), arrowType: ArrowType(ArrowType.ArrowString)) { + case .success(let holder): + let builder = RecordBatch.Builder() + switch builder + .addColumn("test", arrowArray: holder) + .finish() { + case .success(let rb): + XCTAssertEqual(rb.columnCount, 1) + XCTAssertEqual(rb.length, 100) + let col1: Arrow.ArrowArray<String> = rb.data(for: 0) + for index in 0..<col1.length { + if index % 10 == 9 { + XCTAssertEqual(col1[index], nil) + } else { + XCTAssertEqual(col1[index], "test" + String(index)) + } + } + case .failure(let error): + throw error + } + case .failure(let error): + throw error + } + } +} diff --git a/Arrow/Tests/ArrowTests/IPCTests.swift b/Arrow/Tests/ArrowTests/IPCTests.swift index 103c3b2..311a3bb 100644 --- a/Arrow/Tests/ArrowTests/IPCTests.swift +++ b/Arrow/Tests/ArrowTests/IPCTests.swift @@ -98,11 +98,11 @@ func makeRecordBatch() throws -> RecordBatch { floatBuilder.append(433.334) floatBuilder.append(544.445) - let uint8Holder = ArrowArrayHolder(try uint8Builder.finish()) - let stringHolder = ArrowArrayHolder(try stringBuilder.finish()) - let date32Holder = ArrowArrayHolder(try date32Builder.finish()) - let int32Holder = ArrowArrayHolder(try int32Builder.finish()) - let floatHolder = ArrowArrayHolder(try floatBuilder.finish()) + let uint8Holder = ArrowArrayHolderImpl(try uint8Builder.finish()) + let stringHolder = ArrowArrayHolderImpl(try stringBuilder.finish()) + let date32Holder = ArrowArrayHolderImpl(try date32Builder.finish()) + let int32Holder = ArrowArrayHolderImpl(try int32Builder.finish()) + let floatHolder = ArrowArrayHolderImpl(try floatBuilder.finish()) let result = RecordBatch.Builder() .addColumn("col1", arrowArray: uint8Holder) .addColumn("col2", arrowArray: stringHolder) @@ -279,7 +279,7 @@ final class IPCFileReaderTests: XCTestCase { binaryBuilder.append("test33".data(using: .utf8)) binaryBuilder.append("test44".data(using: .utf8)) - let binaryHolder = ArrowArrayHolder(try binaryBuilder.finish()) + let binaryHolder = ArrowArrayHolderImpl(try binaryBuilder.finish()) let result = RecordBatch.Builder() .addColumn("binary", arrowArray: binaryHolder) .finish() @@ -307,8 +307,8 @@ final class IPCFileReaderTests: XCTestCase { time32Builder.append(2) time32Builder.append(nil) time32Builder.append(3) - let time64Holder = ArrowArrayHolder(try time64Builder.finish()) - let time32Holder = ArrowArrayHolder(try time32Builder.finish()) + let time64Holder = ArrowArrayHolderImpl(try time64Builder.finish()) + let time32Holder = ArrowArrayHolderImpl(try time32Builder.finish()) let result = RecordBatch.Builder() .addColumn("time64", arrowArray: time64Holder) .addColumn("time32", arrowArray: time32Holder) diff --git a/Arrow/Tests/ArrowTests/RecordBatchTests.swift b/Arrow/Tests/ArrowTests/RecordBatchTests.swift index 8820f1c..9961781 100644 --- a/Arrow/Tests/ArrowTests/RecordBatchTests.swift +++ b/Arrow/Tests/ArrowTests/RecordBatchTests.swift @@ -29,8 +29,8 @@ final class RecordBatchTests: XCTestCase { stringBuilder.append("test22") stringBuilder.append("test33") - let intHolder = ArrowArrayHolder(try uint8Builder.finish()) - let stringHolder = ArrowArrayHolder(try stringBuilder.finish()) + let intHolder = ArrowArrayHolderImpl(try uint8Builder.finish()) + let stringHolder = ArrowArrayHolderImpl(try stringBuilder.finish()) let result = RecordBatch.Builder() .addColumn("col1", arrowArray: intHolder) .addColumn("col2", arrowArray: stringHolder) diff --git a/Arrow/Tests/ArrowTests/TableTests.swift b/Arrow/Tests/ArrowTests/TableTests.swift index a82a079..8e958cc 100644 --- a/Arrow/Tests/ArrowTests/TableTests.swift +++ b/Arrow/Tests/ArrowTests/TableTests.swift @@ -132,8 +132,8 @@ final class TableTests: XCTestCase { let stringBuilder = try ArrowArrayBuilders.loadStringArrayBuilder() stringBuilder.append("test10") stringBuilder.append("test22") - let intHolder = ArrowArrayHolder(try uint8Builder.finish()) - let stringHolder = ArrowArrayHolder(try stringBuilder.finish()) + let intHolder = ArrowArrayHolderImpl(try uint8Builder.finish()) + let stringHolder = ArrowArrayHolderImpl(try stringBuilder.finish()) let result = RecordBatch.Builder() .addColumn("col1", arrowArray: intHolder) .addColumn("col2", arrowArray: stringHolder) diff --git a/ArrowFlight/Package.swift b/ArrowFlight/Package.swift index f3caa83..629b830 100644 --- a/ArrowFlight/Package.swift +++ b/ArrowFlight/Package.swift @@ -29,7 +29,7 @@ let package = Package( // Products define the executables and libraries a package produces, making them visible to other packages. .library( name: "ArrowFlight", - targets: ["ArrowFlight"]), + targets: ["ArrowFlight"]) ], dependencies: [ .package(url: "https://github.com/grpc/grpc-swift.git", from: "1.15.0"), @@ -48,6 +48,6 @@ let package = Package( ]), .testTarget( name: "ArrowFlightTests", - dependencies: ["ArrowFlight"]), + dependencies: ["ArrowFlight"]) ] ) diff --git a/ArrowFlight/Tests/ArrowFlightTests/FlightTest.swift b/ArrowFlight/Tests/ArrowFlightTests/FlightTest.swift index 8097388..f7bc3c1 100644 --- a/ArrowFlight/Tests/ArrowFlightTests/FlightTest.swift +++ b/ArrowFlight/Tests/ArrowFlightTests/FlightTest.swift @@ -51,9 +51,9 @@ func makeRecordBatch() throws -> RecordBatch { date32Builder.append(date2) date32Builder.append(date1) date32Builder.append(date2) - let doubleHolder = ArrowArrayHolder(try doubleBuilder.finish()) - let stringHolder = ArrowArrayHolder(try stringBuilder.finish()) - let date32Holder = ArrowArrayHolder(try date32Builder.finish()) + let doubleHolder = ArrowArrayHolderImpl(try doubleBuilder.finish()) + let stringHolder = ArrowArrayHolderImpl(try stringBuilder.finish()) + let date32Holder = ArrowArrayHolderImpl(try date32Builder.finish()) let result = RecordBatch.Builder() .addColumn("col1", arrowArray: doubleHolder) .addColumn("col2", arrowArray: stringHolder) diff --git a/CDataWGo/.gitignore b/CDataWGo/.gitignore new file mode 100644 index 0000000..0023a53 --- /dev/null +++ b/CDataWGo/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/CDataWGo/Package.swift b/CDataWGo/Package.swift new file mode 100644 index 0000000..64d29ae --- /dev/null +++ b/CDataWGo/Package.swift @@ -0,0 +1,43 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import PackageDescription + +let package = Package( + name: "go-swift", + platforms: [ + .macOS(.v10_14) + ], + products: [ + .library( + name: "go-swift", + type: .static, + targets: ["go-swift"]) + ], + dependencies: [ + .package(path: "../Arrow") // 👈 Reference to a Local Package + ], + targets: [ + .target( + name: "go-swift", + dependencies: [ + .product(name: "Arrow", package: "Arrow") + ]) + ] +) diff --git a/CDataWGo/Sources/go-swift/CDataTest.swift b/CDataWGo/Sources/go-swift/CDataTest.swift new file mode 100644 index 0000000..b38ca72 --- /dev/null +++ b/CDataWGo/Sources/go-swift/CDataTest.swift @@ -0,0 +1,132 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import Arrow +import ArrowC + +@_cdecl("stringTypeFromSwift") +func stringTypeFromSwift(cSchema: UnsafePointer<ArrowC.ArrowSchema>) { + let unsafePointer = UnsafeMutablePointer(mutating: cSchema) + let exporter = ArrowCExporter() + switch exporter.exportType(&unsafePointer.pointee, arrowType: ArrowType(ArrowType.ArrowString), name: "col1") { + case .success: + return + case .failure(let err): + fatalError("Error exporting string type from swift: \(err)") + } +} + +@_cdecl("stringTypeToSwift") +func stringTypeToSwift(cSchema: UnsafePointer<ArrowC.ArrowSchema>) { + let importer = ArrowCImporter() + switch importer.importField(cSchema.pointee) { + case .success(let field): + if field.name != "col1" { + fatalError("Field name was incorrect expected: col1 but found: \(field.name)") + } + + if field.type.id != ArrowTypeId.string { + fatalError("Field type was incorrect expected: string but found: \(field.type.id)") + } + case .failure(let err): + fatalError("Error importing string type to swift: \(err)") + } +} + +@_cdecl("arrayIntFromSwift") +func arrayIntFromSwift(cArray: UnsafePointer<ArrowC.ArrowArray>) { + do { + let unsafePointer = UnsafeMutablePointer(mutating: cArray) + let arrayBuilder: NumberArrayBuilder<Int32> = try ArrowArrayBuilders.loadNumberArrayBuilder() + for index in 0..<100 { + arrayBuilder.append(Int32(index)) + } + + let array = try arrayBuilder.finish() + let exporter = ArrowCExporter() + exporter.exportArray(&unsafePointer.pointee, arrowData: array.arrowData) + } catch let err { + fatalError("Error exporting array from swift \(err)") + } +} + +@_cdecl("arrayStringFromSwift") +func arrayStringFromSwift(cArray: UnsafePointer<ArrowC.ArrowArray>) { + do { + let unsafePointer = UnsafeMutablePointer(mutating: cArray) + let arrayBuilder = try ArrowArrayBuilders.loadStringArrayBuilder() + for index in 0..<100 { + arrayBuilder.append("test" + String(index)) + } + + let array = try arrayBuilder.finish() + let exporter = ArrowCExporter() + exporter.exportArray(&unsafePointer.pointee, arrowData: array.arrowData) + } catch let err { + fatalError("Error exporting array from swift \(err)") + } +} + +@_cdecl("arrayIntToSwift") +func arrayIntToSwift(cArray: UnsafePointer<ArrowC.ArrowArray>) { + let importer = ArrowCImporter() + switch importer.importArray(cArray, arrowType: ArrowType(ArrowType.ArrowInt32)) { + case .success(let int32Holder): + let result = RecordBatch.Builder() + .addColumn("col1", arrowArray: int32Holder) + .finish() + switch result { + case .success(let recordBatch): + let col1: Arrow.ArrowArray<Int32> = recordBatch.data(for: 0) + for index in 0..<col1.length { + let colVal = col1[index]! + if colVal != index + 1 { + fatalError("Data is incorrect: expected: \(index + 1) but found: \(colVal)") + } + } + case .failure(let err): + fatalError("Error makeing RecordBatch from imported array \(err)") + } + case .failure(let err): + fatalError("Error importing int32 array to swift: \(err)") + } +} + +@_cdecl("arrayStringToSwift") +func arrayStringToSwift(cArray: UnsafePointer<ArrowC.ArrowArray>) { + let importer = ArrowCImporter() + switch importer.importArray(cArray, arrowType: ArrowType(ArrowType.ArrowString)) { + case .success(let dataHolder): + let result = RecordBatch.Builder() + .addColumn("col1", arrowArray: dataHolder) + .finish() + switch result { + case .success(let recordBatch): + let col1: Arrow.ArrowArray<String> = recordBatch.data(for: 0) + for index in 0..<col1.length { + let colVal = col1[index]! + if colVal != "test\(index)" { + fatalError("Data is incorrect: expected: \(index + 1) but found: \(colVal)") + } + } + case .failure(let err): + fatalError("Error makeing RecordBatch from imported array \(err)") + } + case .failure(let err): + fatalError("Error importing int32 array to swift: \(err)") + } +} diff --git a/CDataWGo/go.mod b/CDataWGo/go.mod new file mode 100644 index 0000000..631dd58 --- /dev/null +++ b/CDataWGo/go.mod @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +module go-swift + +go 1.21 + +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/apache/arrow/go/v12 v12.0.1 // indirect + github.com/apache/arrow/go/v16 v16.0.0-20240203105949-22f2cfd1e1eb // indirect + github.com/apache/thrift v0.17.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/flatbuffers v23.5.26+incompatible // indirect + github.com/klauspost/asmfmt v1.3.2 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect + github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect + github.com/pierrec/lz4/v4 v4.1.18 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect +) diff --git a/CDataWGo/go.sum b/CDataWGo/go.sum new file mode 100644 index 0000000..983c3ce --- /dev/null +++ b/CDataWGo/go.sum @@ -0,0 +1,75 @@ +github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/apache/arrow/go/v12 v12.0.1 h1:JsR2+hzYYjgSUkBSaahpqCetqZMr76djX80fF/DiJbg= +github.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw= +github.com/apache/arrow/go/v16 v16.0.0-20240202201540-0fb00fdea7a9 h1:/hG2vtror6DZi7vLBjasliWY+/3GQPnt5FrrLt+boN0= +github.com/apache/arrow/go/v16 v16.0.0-20240202201540-0fb00fdea7a9/go.mod h1:+HkSDKotr3KDBxj7gTVgj8Egy18Y1ECzQdnY5XsXwlQ= +github.com/apache/arrow/go/v16 v16.0.0-20240203105949-22f2cfd1e1eb h1:ox9Zl3OSD9yFHffSUw+mEYVnlC13je3+CGxaR5wIZmA= +github.com/apache/arrow/go/v16 v16.0.0-20240203105949-22f2cfd1e1eb/go.mod h1:+HkSDKotr3KDBxj7gTVgj8Egy18Y1ECzQdnY5XsXwlQ= +github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/apache/thrift v0.17.0 h1:cMd2aj52n+8VoAtvSvLn4kDC3aZ6IAkBuqWQ2IDu7wo= +github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg= +github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= diff --git a/CDataWGo/include/go_swift.h b/CDataWGo/include/go_swift.h new file mode 100644 index 0000000..a7b79ae --- /dev/null +++ b/CDataWGo/include/go_swift.h @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "../../Arrow/Sources/ArrowC/include/ArrowCData.h" + +void stringTypeFromSwift(struct ArrowSchema*); + +void stringTypeToSwift(struct ArrowSchema*); + +void arrayIntFromSwift(struct ArrowArray*); + +void arrayStringFromSwift(struct ArrowArray*); + +void arrayIntToSwift(struct ArrowArray* array); + +void arrayStringToSwift(struct ArrowArray* array); \ No newline at end of file diff --git a/CDataWGo/main.go b/CDataWGo/main.go new file mode 100644 index 0000000..45d1979 --- /dev/null +++ b/CDataWGo/main.go @@ -0,0 +1,127 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package main + +/* +#cgo CFLAGS: -I./include +#cgo LDFLAGS: -L. libgo-swift.a -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx +#cgo LDFLAGS: -L/usr/lib/swift/linux +#include <stdlib.h> +#include "go_swift.h" +*/ +import "C" +import ( + "strconv" + "unsafe" + + "github.com/apache/arrow/go/v16/arrow" + "github.com/apache/arrow/go/v16/arrow/array" + "github.com/apache/arrow/go/v16/arrow/cdata" + "github.com/apache/arrow/go/v16/arrow/memory" +) + +func stringTypeFromSwift() { + arrowSchema := &cdata.CArrowSchema{} + swSchema := (*C.struct_ArrowSchema)(unsafe.Pointer(arrowSchema)) + C.stringTypeFromSwift(swSchema) + gofield, _ := cdata.ImportCArrowField(arrowSchema) + if gofield.Name != "col1" { + panic("Imported type has incorrect name") + } +} + +func stringTypeToSwift() { + arrowSchema := &cdata.CArrowSchema{} + swSchema := (*C.struct_ArrowSchema)(unsafe.Pointer(arrowSchema)) + C.stringTypeFromSwift(swSchema) + gofield, _ := cdata.ImportCArrowField(arrowSchema) + if gofield.Name != "col1" { + panic("Imported type has incorrect name") + } +} + +func arrayStringFromSwift() { + arrowArray := &cdata.CArrowArray{} + swarray := (*C.struct_ArrowArray)(unsafe.Pointer(arrowArray)) + C.arrayStringFromSwift(swarray) + arr, _ := cdata.ImportCArrayWithType(arrowArray, arrow.BinaryTypes.String) + if arr.Len() != 100 { + panic("Array length is incorrect") + } + + for i := 0; i < 100; i++ { + if arr.ValueStr(i) != ("test" + strconv.Itoa(i)) { + panic("Array value is incorrect") + } + } +} + +func arrayIntFromSwift() { + arrowArray := &cdata.CArrowArray{} + swarray := (*C.struct_ArrowArray)(unsafe.Pointer(arrowArray)) + C.arrayIntFromSwift(swarray) + arr, _ := cdata.ImportCArrayWithType(arrowArray, arrow.PrimitiveTypes.Int32) + if arr.Len() != 100 { + panic("Array length is incorrect") + } + + vals := arr.(*array.Int32).Int32Values() + // and that the values are correct + for i, v := range vals { + if v != int32(i) { + panic("Array value is incorrect") + } + } +} + +func arrayIntToSwift() { + bld := array.NewUint32Builder(memory.DefaultAllocator) + defer bld.Release() + bld.AppendValues([]uint32{1, 2, 3, 4}, []bool{true, true, true, true}) + goarray := bld.NewUint32Array() + var carray cdata.CArrowArray + cdata.ExportArrowArray(goarray, &carray, nil) + swarray := (*C.struct_ArrowArray)(unsafe.Pointer(&carray)) + C.arrayIntToSwift(swarray) + + if swarray.release != nil { + panic("Release was not called by swift to deallocate C array") + } +} + +func arrayStringToSwift() { + bld := array.NewStringBuilder(memory.DefaultAllocator) + defer bld.Release() + bld.AppendValues([]string{"test0", "test1", "test2", "test3"}, []bool{true, true, true, true}) + goarray := bld.NewStringArray() + var carray cdata.CArrowArray + cdata.ExportArrowArray(goarray, &carray, nil) + swarray := (*C.struct_ArrowArray)(unsafe.Pointer(&carray)) + C.arrayStringToSwift(swarray) + + if swarray.release != nil { + panic("Release was not called by swift to deallocate C array") + } +} + +func main() { + stringTypeFromSwift() + stringTypeToSwift() + arrayStringFromSwift() + arrayIntFromSwift() + arrayIntToSwift() + arrayStringToSwift() +}
