blob: bd2c4b10f8890d59038e098c6f823e28480beb40 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Note: Imported C structs have a default initializer in Swift that zero-initializes all fields.
// https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html
import Foundation
func mallocOrDie<T>(length: Int) -> UnsafeMutablePointer<T> {
let p = malloc(length)
if p == nil {
fatalError("Couldn't allocate \(length) bytes")
}
return unsafeBitCast(p, UnsafeMutablePointer<T>.self)
}
public extension v23_syncbase_AppPeer {
func extract() -> NeighborhoodPeer {
return NeighborhoodPeer(
appName: appName.extract() ?? "",
blessings: blessings.extract() ?? "",
isLost: isLost)
}
}
public extension v23_syncbase_BatchOptions {
init (_ opts: BatchOptions?) throws {
guard let o = opts else {
hint = v23_syncbase_String()
readOnly = false
return
}
hint = try o.hint?.toCgoString() ?? v23_syncbase_String()
readOnly = o.readOnly
}
}
public extension v23_syncbase_Bytes {
init(_ data: NSData?) {
guard let data = data else {
n = 0
p = nil
return
}
p = mallocOrDie(data.length)
data.getBytes(p, length: data.length)
n = Int32(data.length)
}
// Return value takes ownership of the memory associated with this object.
func extract() -> NSData? {
if p == nil {
return nil
}
return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(p), length: Int(n), freeWhenDone: true)
}
}
public extension v23_syncbase_ChangeType {
func extract() -> WatchChange.ChangeType? {
return WatchChange.ChangeType(rawValue: Int(rawValue))
}
}
public extension v23_syncbase_CollectionRowPattern {
init(_ pattern: CollectionRowPattern) throws {
collectionBlessing = try pattern.collectionBlessing.toCgoString()
collectionName = try pattern.collectionName.toCgoString()
rowKey = try (pattern.rowKey ?? "").toCgoString()
}
}
public extension v23_syncbase_CollectionRowPatterns {
init(_ patterns: [CollectionRowPattern]) throws {
if (patterns.isEmpty) {
n = 0
p = nil
return
}
p = mallocOrDie(patterns.count * sizeof(v23_syncbase_CollectionRowPattern))
n = Int32(patterns.count)
var i = 0
do {
for pattern in patterns {
p.advancedBy(i).memory = try v23_syncbase_CollectionRowPattern(pattern)
i += 1
}
} catch {
free(p)
p = nil
n = 0
throw error
}
}
}
public extension v23_syncbase_EntityType {
func extract() -> WatchChange.EntityType? {
return WatchChange.EntityType(rawValue: Int(rawValue))
}
}
public extension v23_syncbase_Id {
init(_ id: Identifier) throws {
name = try id.name.toCgoString()
blessing = try id.blessing.toCgoString()
}
func extract() -> Identifier? {
guard let name = name.extract(),
blessing = blessing.extract() else {
return nil
}
return Identifier(name: name, blessing: blessing)
}
}
public extension v23_syncbase_Ids {
init(_ ids: [Identifier]) throws {
p = mallocOrDie(ids.count * sizeof(v23_syncbase_Id))
n = Int32(ids.count)
for i in 0 ..< ids.count {
do {
p.advancedBy(i).memory = try v23_syncbase_Id(ids[i])
} catch (let e) {
for j in 0 ..< i {
free(p.advancedBy(j).memory.blessing.p)
free(p.advancedBy(j).memory.name.p)
}
free(p)
throw e
}
}
}
func extract() -> [Identifier] {
var ids: [Identifier] = []
for i in 0 ..< n {
let idStruct = p.advancedBy(Int(i)).memory
if let id = idStruct.extract() {
ids.append(id)
}
}
if p != nil {
free(p)
}
return ids
}
}
public extension v23_syncbase_Invite {
func extract() -> SyncgroupInvite? {
guard let id = syncgroup.extract() else {
return nil
}
return SyncgroupInvite(
syncgroupId: id,
addresses: addresses.extract(),
blessingNames: blessingNames.extract())
}
}
public extension v23_syncbase_Permissions {
init(_ permissions: Permissions?) throws {
guard let p = permissions where !p.isEmpty else {
// Zero-value constructor.
json = v23_syncbase_Bytes()
return
}
var m = [String: AnyObject]()
for (key, value) in p {
m[key as String] = (value as AccessList).toJsonable()
}
let serialized = try NSJSONSerialization.serialize(m)
let bytes = v23_syncbase_Bytes(serialized)
json = bytes
}
func extract() throws -> Permissions? {
guard let data = json.extract(),
map = try NSJSONSerialization.deserialize(data) as? NSDictionary else {
return nil
}
var p = Permissions()
for (k, v) in map {
guard let key = k as? String,
jsonAcessList = v as? [String: AnyObject] else {
throw SyncbaseError.CastError(obj: v)
}
p[key] = AccessList.fromJsonable(jsonAcessList)
}
return p
}
}
public extension v23_syncbase_String {
init(_ string: String) throws {
// TODO: If possible, make one copy instead of two, e.g. using s.getCString.
guard let data = string.dataUsingEncoding(NSUTF8StringEncoding) else {
throw SyncbaseError.InvalidUTF8(invalidUtf8: string)
}
p = mallocOrDie(data.length)
data.getBytes(p, length: data.length)
n = Int32(data.length)
}
// Return value takes ownership of the memory associated with this object.
func extract() -> String? {
if p == nil {
return nil
}
return String(bytesNoCopy: UnsafeMutablePointer<Void>(p),
length: Int(n),
encoding: NSUTF8StringEncoding,
freeWhenDone: true)
}
}
public extension v23_syncbase_Strings {
init(_ strings: [String]) throws {
p = mallocOrDie(strings.count * sizeof(v23_syncbase_String))
n = Int32(strings.count)
for i in 0 ..< strings.count {
do {
p.advancedBy(i).memory = try v23_syncbase_String(strings[i])
} catch (let e) {
for j in 0 ..< i {
free(p.advancedBy(j).memory.p)
}
free(p)
throw e
}
}
}
init(_ strings: [v23_syncbase_String]) {
p = mallocOrDie(strings.count * sizeof(v23_syncbase_String))
n = Int32(strings.count)
var i = 0
for string in strings {
p.advancedBy(i).memory = string
i += 1
}
}
// Return value takes ownership of the memory associated with this object.
func extract() -> [String] {
if p == nil {
return []
}
var ret = [String]()
for i in 0 ..< n {
ret.append(p.advancedBy(Int(i)).memory.extract() ?? "")
}
free(p)
return ret
}
}
public extension String {
/// Create a Cgo-passable string struct forceably (will crash if the string cannot be created).
func toCgoString() throws -> v23_syncbase_String {
return try v23_syncbase_String(self)
}
}
public extension v23_syncbase_SyncgroupMemberInfo {
init(_ info: SyncgroupMemberInfo) {
syncPriority = info.syncPriority
blobDevType = UInt8(info.blobDevType.rawValue)
}
func extract() -> SyncgroupMemberInfo {
return SyncgroupMemberInfo(
syncPriority: syncPriority,
blobDevType: BlobDevType(rawValue: Int(blobDevType))!)
}
}
public extension v23_syncbase_SyncgroupMemberInfoMap {
func extract() -> [String: SyncgroupMemberInfo] {
var ret = [String: SyncgroupMemberInfo]()
for i in 0 ..< Int(n) {
let key = keys.advancedBy(i).memory.extract() ?? ""
let value = values.advancedBy(i).memory.extract()
ret[key] = value
}
free(keys)
free(values)
return ret
}
}
public extension v23_syncbase_SyncgroupSpec {
init(_ spec: SyncgroupSpec) throws {
collections = try v23_syncbase_Ids(spec.collections)
description = try spec.description.toCgoString()
isPrivate = spec.isPrivate
mountTables = try v23_syncbase_Strings(spec.mountTables)
perms = try v23_syncbase_Permissions(spec.permissions)
publishSyncbaseName = try spec.publishSyncbaseName?.toCgoString() ?? v23_syncbase_String()
}
func extract() throws -> SyncgroupSpec {
return SyncgroupSpec(
description: description.extract() ?? "",
collections: collections.extract(),
permissions: try perms.extract() ?? [:],
publishSyncbaseName: publishSyncbaseName.extract(),
mountTables: mountTables.extract(),
isPrivate: isPrivate)
}
}
public extension v23_syncbase_WatchChange {
func extract() -> WatchChange {
let resumeMarkerData = v23_syncbase_Bytes(
p: unsafeBitCast(resumeMarker.p, UnsafeMutablePointer<UInt8>.self),
n: resumeMarker.n).extract()
// Turn row & collectionId zero-values into nil.
var row = self.row.extract()
if row == "" {
row = nil
}
var collectionId = collection.extract()
if collectionId?.name == "" && collectionId?.blessing == "" {
collectionId = nil
}
// Zero-valued Value does not get turned into a nil on put -- if it's a put then we know
// it cannot be nil. This allows us to store empty arrays (an esoteric use case but one that
// is supported nevertheless).
var value = self.value.extract()
if value == nil && changeType == v23_syncbase_ChangeTypePut {
value = NSData()
}
return WatchChange(
entityType: entityType.extract()!,
collectionId: collectionId,
row: row,
changeType: changeType.extract()!,
value: value,
resumeMarker: resumeMarkerData,
isFromSync: fromSync,
isContinued: continued)
}
}
// Note, we don't define init(VError) since we never pass Swift VError objects to Go.
public extension v23_syncbase_VError {
// Return value takes ownership of the memory associated with this object.
func extract() -> VError? {
if id.p == nil {
return nil
}
// Take ownership of all memory before checking optionals.
let vId = id.extract(), vMsg = msg.extract(), vStack = stack.extract()
// TODO: Stop requiring id, msg, and stack to be valid UTF-8?
return VError(id: vId!, actionCode: actionCode, msg: vMsg ?? "", stack: vStack!)
}
}
public struct VError: ErrorType {
public let id: String
public let actionCode: UInt32
public let msg: String
public let stack: String
static func maybeThrow<T>(@noescape f: UnsafeMutablePointer<v23_syncbase_VError> throws -> T) throws -> T {
var e = v23_syncbase_VError()
let res = try f(&e)
if let err = e.extract() {
throw SyncbaseError(err)
}
return res
}
}