The marriage of two beasts
//header.h
char name[] = "IAmAString"; 👌
// file.swift
print(name) // (97, 115, 100, 100, 97, 115, 100, 0) 🤪
//header.h
typedef struct {
char name[5];
int value;
int anotherValue;
} MyStruct;
// file.swift
public struct MyStruct {
var name: [UInt8]
var value: Int32
var anotherValue: Int32
init()
init(name: [UInt8], value: Int32, anotherValue: Int32)
}
//header.h
typedef enum ConnectionError{
ConnectionErrorCouldNotConnect = 0,
ConnectionErrorDisconnected = 1,
ConnectionErrorResetByPeer = 2
}
// file.swift
struct ConnectionError : RawRapresentable, Equatable{ }
var ConnectionErrorCouldNotConnect: ConnectionError {get}
var ConnectionErrorDisconnected: ConnectionError {get}
var ConnectionErrorResetByPeer: ConnectionError {get}
// header.h
#define MY_CONSTANT 42
// file.swift
let MY_CONSTANT 42
print(MemoryLayout<CChar>.stride) // 1 byte
struct AStruct {
let anInt8: Int64
let anInt: Int16
let b: Bool
}
print(MemoryLayout<AStruct>.size)) // 11 (8+2+1) byte
print(MemoryLayout<AStruct>.stride) // 16 (8+4+4) byte
let p: UnsafeMutablePointer<UInt8>? = nil
General rule: mutable pointer instances point to mutable vars.
For class objects (i.e NSDate**), pointers to object passed as pointers translate to AutoreleasingUnsafeMutablePointer
If the type we are pointing to is not completely defined or cannot be represented in Swift, the pointer will be translated to OpaquePointer.
Values pointed to by OpaquePinter cannot be access directly! The pointer variable will have to be converted first.
Untyped pointers get translated to UnsafeRawPointer
All pointer types, except of raw and opaque pointers, are type safe.
The compiler will perform type checks on the pointer and its contents
The compile will also guarantee address alignment with the Pointee type they point to.
The Unsafe prefix has to do with how we access the content.
Interacting with pointers bypasses the general Swift's safety methods so, yes, you can crash something even if it passes compilation.
Once you have a non-void Unsafe*
var anInt:Int = myIntPointer.pointee
myIntPointer.pointee = 42
myIntPointer[0] = 42
var ptr = UnsafeMutablePointer<CChar>.allocate(capacity: 10)
ptr.initialize(from: [CChar](repeating: 0, count: 10))
ptr[3] = 42
ptr.deinitialize()
ptr.deallocate(capacity: 10)
let charPtr = ptr.withMemoryRebound(to: CCChar.self, capacity: 11) {
(cptr) -> String in
return String(validatingUTF8: cptr)!
}
UnsafeRawPointer(typedPointer).bindMemory(to: UIInt8.self,
capacity: 1024)
var ptr = UnsafeMutablePointer<CChar>.allocate(capacity: 5)
ptr.initialize(from: [1,2,3,4,5])
print(ptr.successor().pointee) // 2
print(ptr.advanced(by: 3).pointee) // 4
print(ptr.advanced(by: 3).predecessor().pointee) // 3
aptr.deinitialize()
aptr.deallocate(capacity: 5)
var ptr = UnsafeMutablePointer<CChar>.allocate(capacity: 5)
ptr.initialize(from: [1,2,3,4,5])
print((ptr + 1).pointee) // 2
print((ptr + 3).pointee) // 4
// Passing a Swift string to a libc method
puts("foobar")
var str = "Hello CocoaHeads"
str.withCString { (ptr: UnsafePointer<Int8>) -> Void in
// do something with the pointer
}
// Convert C strings to Swift strings
let swiftString = String(cString: aCString)!
// foobar.c
void aCFunctionWithContext(void *ctx, void (*function)(void *ctx)) {
sleep(10);
function(ctx);
}
class MyClass: CustomStringConvertible {
var property: Int = 0
var description: String { return "MyClass with property \(property)"
}
var myClass = MyClass()
let unmanaged = Unmanaged.passRetained(myClass)
let opaquePtr = unmanaged.toOpaque()
let ptr = UnsafeMutableRawPointer(opaquePtr)
aCFunctionwithContext(ptr) { (p: UnsafeMutableRawPointer?) -> Void in
var c = Unmanaged<MyClass>.fromOpaque(p!).takeUnretainedValue()
c.property = 2
print(c) // "MyClass with property 2"
}
public enum KafkaClientType {
case consumer
case producer
func toRawType() -> rd_kafka_type_t {
switch self {
case .consumer:
return RD_KAFKA_CONSUMER
case .producer:
return RD_KAFKA_PRODUCER
}
}
}
let kSwiftKafkaCStringSize = 1024
let errString = UnsafeMutablePointer<CChar>.allocate(capacity: kSwiftKafkaCStringSize)
defer {
errString.deallocate(capacity: kSwiftKafkaCStringSize)
}
let handle: OpaquePointer = rd_kafka_new(clientType.toRawType(),
globalConfiguration!.handle,
errString,
kSwiftKafkaCStringSize)
public func getMetadata(forTopicHandle topicHandle: OpaquePointer? = nil,
timeout: Int32 = 1000) throws -> Metadata
{
guard let h = handle else {
throw KafkaError.unknownError
}
let ppMetadata = UnsafeMutablePointer<UnsafePointer<rd_kafka_metadata>?>.allocate(capacity: 1)
var error: rd_kafka_resp_err_t
if let topicHandle = topicHandle {
error = rd_kafka_metadata(h, 0, topicHandle, ppMetadata, timeout)
} else {
error = rd_kafka_metadata(h, 1, nil, ppMetadata, timeout)
}
guard error == RD_KAFKA_RESP_ERR_NO_ERROR else {
throw KafkaError.coreError(KafkaCoreError(error))
}
guard let rawMetadata = (ppMetadata.pointee)?.pointee else {
throw KafkaError.unknownError
}
rd_kafka_metadata_destroy(ppMetadata.pointee)
ppMetadata.deallocate(capacity: 1)
return Metadata.metadata(fromRawMetadata: rawMetadata)
}
extension Metadata {
static func metadata(fromRawMetadata data: rd_kafka_metadata) -> Metadata {
let brokers = BrokerMetadata.brokers(fromRawBrokersData: data.brokers,
numOfBrokers: data.broker_cnt)
let topics = TopicMetadata.topics(fromRawTopicsData: data.topics,
numOfTopics: data.topic_cnt)
return Metadata(brokers: brokers,
topics: topics,
originatingBrokerId: Int(data.orig_broker_id),
originatingBrokerName: String(cString: data.orig_broker_name))
}
}
public struct BrokerMetadata {
public let id: Int
public let host: String
public let port: Int
}
extension BrokerMetadata {
static func brokers(fromRawBrokersData data: UnsafeMutablePointer<rd_kafka_metadata_broker>,
numOfBrokers: Int32) -> [BrokerMetadata]
{
var brokers: [BrokerMetadata] = []
guard numOfBrokers > 0 else {
return brokers
}
for i in 0...numOfBrokers - 1 {
let b = data.advanced(by: Int(i)).pointee
let broker = BrokerMetadata(id: Int(b.id),
host: String(cString: b.host),
port: Int(b.port))
brokers.append(broker)
}
return brokers
}
}
// Package.swift
let package = Package(
name: "ckafka",
// Name of the pkg-config (.pc) file to get the
// additional flags for system modules.
pkgConfig: "rdkafka",
providers: [
.Brew("librdkafka"),
.Apt("librdkafka-dev")
]
)
// Module.modulemap
module ckafka [system] {
header "ckafka.h"
link "rdkafka"
export *
}