swift-cgo: Implement scan/delete/batch/permission/get/put

Various refactors, formatting, and bug fixes plus:

Implements:
	Collection.Scan
	Collection.DeleteRange
	Collection.get/put using user-supplied byte arrays*
	Database.watch using user-supplied byte arrays*
	Database.setPermission/getPermission
	Service.setPermission/getPermission
	Collection.getPermission/setPermission
	Batch.runInBatch
	Database.beginBatch / BatchDatabase.abort/commit

Unit tests:
	Scan
	Delete
	Get/put
	Batch operations

Missing unit tests:
	None for permissions

* Removes the JSON support for arbitrary byte arrays as a stop
gap until VOM is implemented in Swift.

MultiPart: 2/2
Change-Id: I1cef38dbf3aa444d1a7fb6f679ccfa022cbd9206
diff --git a/SyncbaseCore/Source/Batch.swift b/SyncbaseCore/Source/Batch.swift
index f3bf94b..febd0f8 100644
--- a/SyncbaseCore/Source/Batch.swift
+++ b/SyncbaseCore/Source/Batch.swift
@@ -4,23 +4,76 @@
 
 import Foundation
 
-class BatchHandle {
-}
-
 public enum Batch {
-  public typealias BatchCompletionHandler = SyncbaseError? -> Void
-  public typealias Operation = Void -> BatchCompletionHandler
+  public typealias BatchCompletionHandler = ErrorType? -> Void
+  public typealias Operation = BatchDatabase throws -> Void
 
   /**
    Runs the given batch operation, managing retries and BatchDatabase's commit() and abort()s.
 
-   - Parameter db:      database on which the batch operation is to be performed
-   - Parameter opts:    batch configuration
-   - Parameter op:      batch operation
+   This is run in a background thread and calls back on main.
+
+   - Parameter retries:      number of retries attempted before giving up. defaults to 3
+   - Parameter db:           database on which the batch operation is to be performed
+   - Parameter opts:         batch configuration
+   - Parameter op:           batch operation
    - Parameter completionHandler:      future result called when runInBatch finishes
    */
-  public static func runInBatch(db: Database, opts: BatchOptions, op: Operation, completionHandler: BatchCompletionHandler) {
-    preconditionFailure("stub")
+  public static func runInBatch(retries: Int = 3,
+    db: Database,
+    opts: BatchOptions?,
+    op: Operation,
+    completionHandler: BatchCompletionHandler) {
+      RunInBackgroundQueue {
+        for _ in 0...retries {
+          // TODO(sadovsky): Commit() can fail for a number of reasons, e.g. RPC
+          // failure or ErrConcurrentTransaction. Depending on the cause of failure,
+          // it may be desirable to retry the Commit() and/or to call Abort().
+          var err: ErrorType? = nil
+          do {
+            try attemptBatch(db, opts: opts, op: op)
+            err = nil
+          } catch SyncbaseError.ConcurrentBatch {
+            continue
+          } catch let e {
+            log.warning("Unable to complete batch operation: \(e)")
+            err = e
+          }
+          RunInMainQueue { completionHandler(err) }
+          return
+        }
+        // We never were able to do it without error
+        RunInMainQueue { completionHandler(SyncbaseError.ConcurrentBatch) }
+      }
+  }
+
+  private static func attemptBatch(db: Database, opts: BatchOptions?, op: Operation) throws {
+    let batchDb = try db.beginBatch(opts)
+    // Use defer for abort to make sure it gets called in case op throws.
+    var commitCalled = false
+    defer {
+      if !commitCalled {
+        do {
+          try batchDb.abort()
+        } catch let e {
+          log.warning("Unable abort the non-comitted batch: \(e)")
+        }
+      }
+    }
+    // Attempt operation
+    try op(batchDb)
+    // A readonly batch should be Aborted; Commit would fail.
+    if opts?.readOnly ?? false {
+      return
+    }
+    // Commit is about to be called, do not call Abort.
+    commitCalled = true
+    do {
+      try batchDb.commit()
+    } catch SyncbaseError.UnknownBatch {
+      // Occurs if op called batchDb.abort() or batchDb.commit() -- ignore the unknown batch
+      // at this point.
+    }
   }
 }
 
@@ -37,14 +90,34 @@
   public let readOnly: Bool = false
 }
 
-public protocol BatchDatabase: DatabaseHandle {
+public class BatchDatabase: Database {
   /// Commit persists the pending changes to the database.
   /// If the batch is readonly, Commit() will fail with ErrReadOnlyBatch; Abort()
   /// should be used instead.
-  func commit() throws
+  public func commit() throws {
+    guard let cHandle = try batchHandle?.toCgoString() else {
+      throw SyncbaseError.UnknownBatch
+    }
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_DbCommit(
+        try encodedDatabaseName.toCgoString(),
+        cHandle,
+        errPtr)
+    }
+  }
 
   /// Abort notifies the server that any pending changes can be discarded.
   /// It is not strictly required, but it may allow the server to release locks
   /// or other resources sooner than if it was not called.
-  func abort() throws
+  public func abort() throws {
+    guard let cHandle = try batchHandle?.toCgoString() else {
+      throw SyncbaseError.UnknownBatch
+    }
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_DbAbort(
+        try encodedDatabaseName.toCgoString(),
+        cHandle,
+        errPtr)
+    }
+  }
 }
diff --git a/SyncbaseCore/Source/Collection.swift b/SyncbaseCore/Source/Collection.swift
index 8ce81d1..269c34a 100644
--- a/SyncbaseCore/Source/Collection.swift
+++ b/SyncbaseCore/Source/Collection.swift
@@ -27,15 +27,10 @@
   let batchHandle: String?
   let encodedCollectionName: String
 
-  init?(databaseId: Identifier, collectionId: Identifier, batchHandle: String?) {
+  init(databaseId: Identifier, collectionId: Identifier, batchHandle: String?) throws {
     self.collectionId = collectionId
     self.batchHandle = batchHandle
-    do {
-      self.encodedCollectionName = try Collection.encodedName(databaseId, collectionId: collectionId)
-    } catch {
-      // UTF8 encoding error.
-      return nil
-    }
+    self.encodedCollectionName = try Collection.encodedName(databaseId, collectionId: collectionId)
   }
 
   /// Exists returns true only if this Collection exists. Insufficient
@@ -58,13 +53,10 @@
   /// TODO(sadovsky): Specify what happens if perms is nil.
   public func create(permissions: Permissions?) throws {
     try VError.maybeThrow { errPtr in
-      guard let cPermissions = v23_syncbase_Permissions(permissions) else {
-        throw SyncbaseError.PermissionsSerializationError(permissions: permissions)
-      }
       v23_syncbase_CollectionCreate(
         try encodedCollectionName.toCgoString(),
         try cBatchHandle(),
-        cPermissions,
+        try v23_syncbase_Permissions(permissions),
         errPtr)
     }
   }
@@ -86,12 +78,27 @@
   /// receive the rows in this Collection. It only determines which clients
   /// are allowed to retrieve the value using a Syncbase RPC.
   public func getPermissions() throws -> Permissions {
-    preconditionFailure("stub")
+    var permissions = v23_syncbase_Permissions()
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_CollectionGetPermissions(
+        try encodedCollectionName.toCgoString(),
+        try cBatchHandle(),
+        &permissions,
+        errPtr)
+    }
+    // TODO(zinman): Verify that permissions defaulting to zero-value is correct.
+    return try permissions.toPermissions() ?? Permissions()
   }
 
   /// SetPermissions replaces the current Permissions for the Collection.
   public func setPermissions(permissions: Permissions) throws {
-    preconditionFailure("stub")
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_CollectionSetPermissions(
+        try encodedCollectionName.toCgoString(),
+        try cBatchHandle(),
+        try v23_syncbase_Permissions(permissions),
+        errPtr)
+    }
   }
 
   /**
@@ -113,37 +120,50 @@
    let isRed: Bool = try collection.get("isRed")
    ```
    */
-  public func get<T: SyncbaseJsonConvertible>(key: String, inout value: T?) throws {
+  public func get<T: SyncbaseConvertible>(key: String, inout value: T?) throws {
     // TODO(zinman): We should probably kill this variant unless it provides .dynamicType benefits
     // with VOM support and custom class serialization/deserialization.
     value = try get(key)
   }
 
   /// Get loads the value stored under the given key.
-  public func get<T: SyncbaseJsonConvertible>(key: String) throws -> T? {
-    guard let jsonData = try getRawBytes(key) else {
+  public func get<T: SyncbaseConvertible>(key: String) throws -> T? {
+    guard let data = try getRawBytes(key) else {
       return nil
     }
-    let value: T = try T.fromSyncbaseJson(jsonData)
+    let value: T = try T.deserializeFromSyncbase(data)
     return value
   }
 
   func getRawBytes(key: String) throws -> NSData? {
     var cBytes = v23_syncbase_Bytes()
-    try VError.maybeThrow { errPtr in
-      v23_syncbase_RowGet(try encodedRowName(key),
-        try cBatchHandle(),
-        &cBytes,
-        errPtr)
+    do {
+      try VError.maybeThrow { errPtr in
+        v23_syncbase_RowGet(
+          try encodedRowName(key),
+          try cBatchHandle(),
+          &cBytes,
+          errPtr)
+      }
+    } catch let e as VError {
+      if e.id == "v.io/v23/verror.NoExist" {
+        return nil
+      }
+      throw e
     }
-    return cBytes.toNSData()
+    // If we got here then we know that row exists, otherwise we would have gotten the NoExist
+    // exception above. However, that row might also just be empty data. Because
+    // cBytes.toNSData can't distinguish between the struct's zero-values and a nil array,
+    // we must explicitly default to an empty NSData here since we know that it is not nil.
+    return cBytes.toNSData() ?? NSData()
   }
 
   /// Put writes the given value to this Collection under the given key.
-  public func put(key: String, value: SyncbaseJsonConvertible) throws {
-    let (data, _) = try value.toSyncbaseJson()
+  public func put(key: String, value: SyncbaseConvertible) throws {
+    let data = try value.serializeToSyncbase()
     try VError.maybeThrow { errPtr in
-      v23_syncbase_RowPut(try encodedRowName(key),
+      v23_syncbase_RowPut(
+        try encodedRowName(key),
         try cBatchHandle(),
         v23_syncbase_Bytes(data),
         errPtr)
@@ -153,7 +173,8 @@
   /// Delete deletes the row for the given key.
   public func delete(key: String) throws {
     try VError.maybeThrow { errPtr in
-      v23_syncbase_RowDelete(try encodedRowName(key),
+      v23_syncbase_RowDelete(
+        try encodedRowName(key),
         try cBatchHandle(),
         errPtr)
     }
@@ -166,7 +187,20 @@
   /// deletions?
   /// See helpers Prefix(), Range(), SingleRow().
   public func deleteRange(r: RowRange) throws {
-    preconditionFailure("stub")
+    try VError.maybeThrow { errPtr in
+      let cStartStr = try r.start.toCgoString()
+      let cLimitStr = try r.limit.toCgoString()
+      let cStartBytes = v23_syncbase_Bytes(
+        p: unsafeBitCast(cStartStr.p, UnsafeMutablePointer<UInt8>.self), n: cStartStr.n)
+      let cLimitBytes = v23_syncbase_Bytes(
+        p: unsafeBitCast(cLimitStr.p, UnsafeMutablePointer<UInt8>.self), n: cLimitStr.n)
+      v23_syncbase_CollectionDeleteRange(
+        try encodedCollectionName.toCgoString(),
+        try cBatchHandle(),
+        cStartBytes,
+        cLimitBytes,
+        errPtr)
+    }
   }
 
   /// Scan returns all rows in the given half-open range [start, limit). If limit
@@ -176,8 +210,128 @@
   /// time of the RPC (or at the time of BeginBatch, if in a batch), and will not
   /// reflect subsequent writes to keys not yet reached by the stream.
   /// See helpers Prefix(), Range(), SingleRow().
-  public func scan(r: RowRange) -> ScanStream {
-    preconditionFailure("stub")
+  private static let scanQueue = dispatch_queue_create("ScanQueue", DISPATCH_QUEUE_CONCURRENT)
+  public func scan(r: RowRange) throws -> ScanStream {
+    // Scan works by having Go call Swift as encounters each row. This is a bit of a mismatch
+    // for the Swift GeneratorType which is pull instead of push-based. We create a push-pull
+    // adapter by blocking on either side using condition variables until both are ready for the
+    // next data handoff. Adding to the complexity is that Go has separate callbacks for when it has
+    // data or is done, yet the GeneratorType uses a single fetch function that handles both
+    // conditions (the data is nil when it's done). Thus we end up with 2 different callbacks from
+    // Go with similar condition-variable logic.
+    let condition = NSCondition()
+    var data: (String, NSData)? = nil
+    var doneErr: ErrorType? = nil
+    var updateAvailable = false
+
+    // The anonymous function that gets called from the Swift. It blocks until there's an update
+    // available from Go.
+    let fetchNext = { () -> ((String, GetValueFromScanStream)?, ErrorType?) in
+      condition.lock()
+      while !updateAvailable {
+        condition.wait()
+      }
+      // Grab the data from this update and reset for the next update.
+      let fetchedData = data
+      data = nil
+      updateAvailable = false
+      // Signal that we've fetched the data to Go.
+      condition.signal()
+      condition.unlock()
+      // Default the ret to nil (valid for isDone).
+      var ret: (String, GetValueFromScanStream)? = nil
+      if let d = fetchedData {
+        // Create the closured function that deserializes the data from Syncbase on demand.
+        ret = (d.0, { () throws -> SyncbaseConvertible in
+          // Let T be inferred by being explicit about NSData (the only conversion possible until
+          // we have VOM support).
+          let data: NSData = try NSData.deserializeFromSyncbase(d.1)
+          return data
+        })
+      }
+      return (ret, doneErr)
+    }
+
+    // The callback from Go when there's a new Row (key-value) scanned.
+    let onKV = { (key: String, valueBytes: NSData) in
+      condition.lock()
+      // Wait until any existing update has been received by the fetch so we don't just blow
+      // past it.
+      while updateAvailable {
+        condition.wait()
+      }
+      // Set the new data.
+      data = (key, valueBytes)
+      updateAvailable = true
+      // Wake up any blocked fetch.
+      condition.signal()
+      condition.unlock()
+    }
+
+    let onDone = { (err: ErrorType?) in
+      condition.lock()
+      // Wait until any existing update has been received by the fetch so we don't just blow
+      // past it.
+      while updateAvailable {
+        condition.wait()
+      }
+      // Marks the end of data by clearing it and saving any associated error from Syncbase.
+      data = nil
+      doneErr = err
+      updateAvailable = true
+      // Wake up any blocked fetch.
+      condition.signal()
+      condition.unlock()
+    }
+
+    try VError.maybeThrow { errPtr in
+      let cStartStr = try r.start.toCgoString()
+      let cLimitStr = try r.limit.toCgoString()
+      let cStartBytes = v23_syncbase_Bytes(
+        p: unsafeBitCast(cStartStr.p, UnsafeMutablePointer<UInt8>.self), n: cStartStr.n)
+      let cLimitBytes = v23_syncbase_Bytes(
+        p: unsafeBitCast(cLimitStr.p, UnsafeMutablePointer<UInt8>.self), n: cLimitStr.n)
+      let callbacks = v23_syncbase_CollectionScanCallbacks(
+        hOnKeyValue: Collection.onScanKVClosures.ref(onKV),
+        hOnDone: Collection.onScanDoneClosures.ref(onDone),
+        onKeyValue: { Collection.onScanKV(AsyncId($0), kv: $1) },
+        onDone: { Collection.onScanDone(AsyncId($0), doneHandle: AsyncId($1), err: $2) })
+      v23_syncbase_CollectionScan(
+        try encodedCollectionName.toCgoString(),
+        try cBatchHandle(),
+        cStartBytes,
+        cLimitBytes,
+        callbacks,
+        errPtr)
+    }
+
+    return AnonymousStream(fetchNextFunction: fetchNext, cancelFunction: { })
+  }
+
+  // Reference maps between closured functions and handles passed back/forth with Go.
+  private static var onScanKVClosures = RefMap < (String, NSData) -> Void > ()
+  private static var onScanDoneClosures = RefMap < ErrorType? -> Void > ()
+
+  // Callback handlers that convert the Cgo bridge types to native Swift types and pass them to
+  // the closured functions reference by the passed handle.
+  private static func onScanKV(handle: AsyncId, kv: v23_syncbase_KeyValue) {
+    guard let key = kv.key.toString(),
+      valueBytes = kv.value.toNSData(),
+      callback = onScanKVClosures.get(handle) else {
+        log.warning("Could not fully unpact scan kv callback; dropping")
+        return
+    }
+    callback(key, valueBytes)
+  }
+
+  private static func onScanDone(kvHandle: AsyncId, doneHandle: AsyncId, err: v23_syncbase_VError) {
+    let e = err.toVError()
+    onScanKVClosures.unref(kvHandle)
+    guard let callback = onScanDoneClosures.unref(doneHandle) else {
+      log.warning("Could not find closure for onDone handle; dropping")
+      return
+    }
+    callback(e)
   }
 
   // MARK: Internal helpers
@@ -206,7 +360,7 @@
 }
 
 /// Returns the decoded value, or throws an error if the value could not be decoded.
-public typealias GetValueFromScanStream = () throws -> SyncbaseJsonConvertible
+public typealias GetValueFromScanStream = () throws -> SyncbaseConvertible
 
 /// Stream resulting from a scan on a scollection for a given row range.
 public typealias ScanStream = AnonymousStream<(String, GetValueFromScanStream)>
diff --git a/SyncbaseCore/Source/Database.swift b/SyncbaseCore/Source/Database.swift
index effac29..6101a02 100644
--- a/SyncbaseCore/Source/Database.swift
+++ b/SyncbaseCore/Source/Database.swift
@@ -32,25 +32,20 @@
   let batchHandle: String?
   let encodedDatabaseName: String
 
-  init?(databaseId: Identifier, batchHandle: String?) {
+  init(databaseId: Identifier, batchHandle: String?) throws {
     self.databaseId = databaseId
     self.batchHandle = batchHandle
-    do {
-      self.encodedDatabaseName = try databaseId.encodeId().toString()!
-    } catch {
-      // UTF8 encoding error.
-      return nil
-    }
+    self.encodedDatabaseName = try databaseId.encodeId().toString()!
   }
 
   /// Create creates this Database.
   /// TODO(sadovsky): Specify what happens if perms is nil.
   public func create(permissions: Permissions?) throws {
     try VError.maybeThrow { errPtr in
-      guard let cPermissions = v23_syncbase_Permissions(permissions) else {
-        throw SyncbaseError.PermissionsSerializationError(permissions: permissions)
-      }
-      v23_syncbase_DbCreate(try encodedDatabaseName.toCgoString(), cPermissions, errPtr)
+      v23_syncbase_DbCreate(
+        try encodedDatabaseName.toCgoString(),
+        try v23_syncbase_Permissions(permissions),
+        errPtr)
     }
   }
 
@@ -96,7 +91,18 @@
    TODO(sadovsky): Use varargs for options.
    */
   public func beginBatch(options: BatchOptions?) throws -> BatchDatabase {
-    preconditionFailure("stub")
+    var cHandle = v23_syncbase_String()
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_DbBeginBatch(
+        try encodedDatabaseName.toCgoString(),
+        try v23_syncbase_BatchOptions(options),
+        &cHandle,
+        errPtr)
+    }
+    guard let handle = cHandle.toString() else {
+      throw SyncbaseError.InvalidUTF8(invalidUtf8: "\(cHandle)")
+    }
+    return try BatchDatabase(databaseId: databaseId, batchHandle: handle)
   }
 
   /// Watch allows a client to watch for updates to the database. For each watch
@@ -161,19 +167,17 @@
   }
 
   public func collection(collectionId: Identifier) throws -> Collection {
-    guard let collection = Collection(
+    return try Collection(
       databaseId: databaseId,
       collectionId: collectionId,
-      batchHandle: batchHandle) else {
-        throw SyncbaseError.InvalidUTF8(invalidUtf8: "\(collectionId)")
-    }
-    return collection
+      batchHandle: batchHandle)
   }
 
   public func listCollections() throws -> [Identifier] {
     return try VError.maybeThrow { errPtr in
       var ids = v23_syncbase_Ids()
-      v23_syncbase_DbListCollections(try encodedDatabaseName.toCgoString(),
+      v23_syncbase_DbListCollections(
+        try encodedDatabaseName.toCgoString(),
         try batchHandle?.toCgoString() ?? v23_syncbase_String(),
         &ids,
         errPtr)
@@ -182,16 +186,41 @@
   }
 
   public func getResumeMarker() throws -> ResumeMarker {
-    preconditionFailure("stub")
+    var cMarker = v23_syncbase_Bytes()
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_DbGetResumeMarker(
+        try encodedDatabaseName.toCgoString(),
+        try batchHandle?.toCgoString() ?? v23_syncbase_String(),
+        &cMarker,
+        errPtr)
+    }
+    return ResumeMarker(data: cMarker.toNSData() ?? NSData())
   }
 }
 
 extension Database: AccessController {
-  public func setPermissions(perms: Permissions, version: PermissionsVersion) throws {
-    preconditionFailure("stub")
+  public func getPermissions() throws -> (Permissions, PermissionsVersion) {
+    var cPermissions = v23_syncbase_Permissions()
+    var cVersion = v23_syncbase_String()
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_DbGetPermissions(
+        try encodedDatabaseName.toCgoString(),
+        &cPermissions,
+        &cVersion,
+        errPtr)
+    }
+    // TODO(zinman): Verify that permissions defaulting to zero-value is correct for Permissions.
+    // We force cast of cVersion because we know it can be UTF8 converted.
+    return (try cPermissions.toPermissions() ?? Permissions(), cVersion.toString()!)
   }
 
-  public func getPermissions() throws -> (Permissions, PermissionsVersion) {
-    preconditionFailure("stub")
+  public func setPermissions(permissions: Permissions, version: PermissionsVersion) throws {
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_DbSetPermissions(
+        try encodedDatabaseName.toCgoString(),
+        try v23_syncbase_Permissions(permissions),
+        try version.toCgoString(),
+        errPtr)
+    }
   }
 }
diff --git a/SyncbaseCore/Source/Errors.swift b/SyncbaseCore/Source/Errors.swift
index 5d75ba9..755cfd1 100644
--- a/SyncbaseCore/Source/Errors.swift
+++ b/SyncbaseCore/Source/Errors.swift
@@ -14,29 +14,50 @@
   case BlobNotCommitted
   case SyncgroupJoinFailed
   case BadExecStreamHeader
+  case InvalidPermissionsChange
   case InvalidName(name: String)
   case CorruptDatabase(path: String)
   case InvalidOperation(reason: String)
-  case PermissionsSerializationError(permissions: Permissions?)
   case InvalidUTF8(invalidUtf8: String)
+  case CastError(obj: Any)
+
+  init?(_ err: VError) {
+    // TODO(zinman): Make VError better by having the proper arguments transmitted across
+    // so we don't have to use err.msg to repeat our messages.
+    switch err.id {
+    case "v.io/v23/services/syncbase.NotInDevMode": self = SyncbaseError.NotInDevMode
+    case "v.io/v23/services/syncbase.InvalidName": self = SyncbaseError.InvalidName(name: err.msg)
+    case "v.io/v23/services/syncbase.CorruptDatabase": self = SyncbaseError.CorruptDatabase(path: err.msg)
+    case "v.io/v23/services/syncbase.UnknownBatch": self = SyncbaseError.UnknownBatch
+    case "v.io/v23/services/syncbase.NotBoundToBatch": self = SyncbaseError.NotBoundToBatch
+    case "v.io/v23/services/syncbase.ReadOnlyBatch": self = SyncbaseError.ReadOnlyBatch
+    case "v.io/v23/services/syncbase.ConcurrentBatch": self = SyncbaseError.ConcurrentBatch
+    case "v.io/v23/services/syncbase.BlobNotCommitted": self = SyncbaseError.BlobNotCommitted
+    case "v.io/v23/services/syncbase.SyncgroupJoinFailed": self = SyncbaseError.SyncgroupJoinFailed
+    case "v.io/v23/services/syncbase.BadExecStreamHeader": self = SyncbaseError.BadExecStreamHeader
+    case "v.io/v23/services/syncbase.InvalidPermissionsChange": self = SyncbaseError.InvalidPermissionsChange
+    default: return nil
+    }
+  }
 
   public var description: String {
     switch (self) {
-    case .NotAuthorized: return "no valid blessings; create new blessings using oauth"
-    case .NotInDevMode: return "not running with --dev=true"
-    case .UnknownBatch: return "unknown batch, perhaps the server restarted"
-    case .NotBoundToBatch: return "not bound to batch"
-    case .ReadOnlyBatch: return "batch is read-only"
-    case .ConcurrentBatch: return "concurrent batch"
-    case .BlobNotCommitted: return "blob is not yet committed"
-    case .SyncgroupJoinFailed: return "syncgroup join failed"
+    case .NotAuthorized: return "No valid blessings; create new blessings using oauth"
+    case .NotInDevMode: return "Not running with --dev=true"
+    case .UnknownBatch: return "Unknown batch, perhaps the server restarted"
+    case .NotBoundToBatch: return "Not bound to batch"
+    case .ReadOnlyBatch: return "Batch is read-only"
+    case .ConcurrentBatch: return "Concurrent batch"
+    case .BlobNotCommitted: return "Blob is not yet committed"
+    case .SyncgroupJoinFailed: return "Syncgroup join failed"
     case .BadExecStreamHeader: return "Exec stream header improperly formatted"
-    case .InvalidName(let name): return "invalid name: \(name)"
+    case .InvalidPermissionsChange: return "The sequence of permission changes is invalid"
+    case .InvalidName(let name): return "Invalid name: \(name)"
     case .CorruptDatabase(let path):
-      return "database corrupt, moved to path \(path); client must create a new database"
-    case .InvalidOperation(let reason): return "invalid operation: \(reason)"
-    case .PermissionsSerializationError(let permissions): return "unable to serialize permissions: \(permissions)"
-    case .InvalidUTF8(let invalidUtf8): return "unable to convert to utf8: \(invalidUtf8)"
+      return "Database corrupt, moved to path \(path); client must create a new database"
+    case .InvalidOperation(let reason): return "Invalid operation: \(reason)"
+    case .InvalidUTF8(let invalidUtf8): return "Unable to convert to utf8: \(invalidUtf8)"
+    case .CastError(let obj): return "Unable to convert to cast: \(obj)"
     }
   }
-}
\ No newline at end of file
+}
diff --git a/SyncbaseCore/Source/Locking.swift b/SyncbaseCore/Source/Locking.swift
new file mode 100644
index 0000000..c73dd61
--- /dev/null
+++ b/SyncbaseCore/Source/Locking.swift
@@ -0,0 +1,17 @@
+// 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.
+
+import Foundation
+
+protocol Lockable {
+  func lock(block:()->())
+}
+
+extension Lockable where Self : AnyObject {
+  func lock(block:()->()) {
+    objc_sync_enter(self)
+    block()
+    objc_sync_exit(self)
+  }
+}
\ No newline at end of file
diff --git a/SyncbaseCore/Source/Marshal.swift b/SyncbaseCore/Source/Marshal.swift
index 618b012..f5b67c0 100644
--- a/SyncbaseCore/Source/Marshal.swift
+++ b/SyncbaseCore/Source/Marshal.swift
@@ -47,183 +47,22 @@
   }
 }
 
-public enum JsonDataType: Int {
-  case Bool = 0,
-    Int, Int8, Int16, Int32, Int64,
-    UInt, UInt8, UInt16, UInt32, UInt64,
-    Float, Double,
-    String,
-    Array, Dictionary,
-    RawJson
+public protocol SyncbaseConvertible {
+  func serializeToSyncbase() throws -> NSData
+  static func deserializeFromSyncbase<T: SyncbaseConvertible>(data: NSData) throws -> T
 }
 
-public protocol SyncbaseJsonConvertible {
-  func toSyncbaseJson() throws -> (NSData, JsonDataType)
-  static func fromSyncbaseJson<T: SyncbaseJsonConvertible>(data: NSData) throws -> T
-}
-
-extension SyncbaseJsonConvertible {
-  public static func fromSyncbaseJson<T: SyncbaseJsonConvertible>(data: NSData) throws -> T {
-    let obj = try NSJSONSerialization.deserialize(data)
-    var target: T? = nil
-    if NSNumber.isNSNumber(obj) {
-      let nsnumber = obj as! NSNumber
-      if !nsnumber.isTargetCastable(&target) {
-        throw JsonErrors.CastError(value: nsnumber, target: target)
-      }
-      return nsnumber as! T
-    }
-    if let cast = obj as? T {
+extension SyncbaseConvertible {
+  public static func deserializeFromSyncbase<T: SyncbaseConvertible>(data: NSData) throws -> T {
+    if let cast = data as? T {
       return cast
     }
-    throw JsonErrors.CastError(value: obj, target: target)
+    throw SyncbaseError.CastError(obj: data)
   }
 }
 
-extension Bool: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.Bool)
-  }
-}
-
-// Wish we could put an extension on a protocol like IntegerLiterableConvertable or IntegerType
-// but we can't as of Swift 2.2, so we enumerate all the possiblities
-
-extension Int: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.Int)
-  }
-}
-
-extension Int8: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(Int(self)), JsonDataType.Int8)
-  }
-}
-
-extension Int16: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(Int(self)), JsonDataType.Int16)
-  }
-}
-
-extension Int32: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(Int(self)), JsonDataType.Int32)
-  }
-}
-
-extension Int64: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(NSNumber(longLong: self)), JsonDataType.Int64)
-  }
-}
-
-extension UInt: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.UInt)
-  }
-}
-
-extension UInt8: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(UInt(self)), JsonDataType.UInt8)
-  }
-}
-
-extension UInt16: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(UInt(self)), JsonDataType.UInt16)
-  }
-}
-
-extension UInt32: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(UInt(self)), JsonDataType.UInt32)
-  }
-}
-
-extension UInt64: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(NSNumber(unsignedLongLong: self)), JsonDataType.UInt64)
-  }
-}
-
-extension Float: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.Float)
-  }
-}
-
-extension Double: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.Double)
-  }
-}
-
-extension String: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try NSJSONSerialization.hackSerializeAnyObject(self), JsonDataType.String)
-  }
-}
-
-// Wish we could do this:
-// extension Array : SyncbaseJsonConvertible where Element: AnyObject {
-// but it's not valid Swift 2.2
-
-extension Array where Element: AnyObject {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try! NSJSONSerialization.dataWithJSONObject(self, options: []), JsonDataType.Array)
-  }
-}
-
-extension NSArray: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try! NSJSONSerialization.dataWithJSONObject(self, options: []), JsonDataType.Array)
-  }
-}
-
-extension NSDictionary: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (try! NSJSONSerialization.dataWithJSONObject(self, options: []), JsonDataType.Dictionary)
-  }
-}
-
-// Annoyingly we can't directly cast an Array that we have no type information on into an AnyObject
-// or similarly the same for Dictionary. So instead we're forced to copy all the elements and test
-// all individual types inside.
-extension Array: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    let copy: [AnyObject] = try self.map { elem in
-      guard let jsonable = elem as? AnyObject else {
-        throw JsonErrors.ArrayContainsInvalidTypes
-      }
-      return jsonable
-    }
-    let data = try NSJSONSerialization.serialize(copy)
-    return (data, JsonDataType.Array)
-  }
-}
-
-extension Dictionary: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    let copy = NSMutableDictionary()
-    try self.forEach { (key, value) in
-      guard let strKey = key as? String else {
-        throw JsonErrors.DictionaryContainsInvalidTypes
-      }
-      guard let jsonable = value as? AnyObject else {
-        throw JsonErrors.DictionaryContainsInvalidTypes
-      }
-      copy[strKey] = jsonable
-    }
-    let data = try NSJSONSerialization.serialize(copy)
-    return (data, JsonDataType.Dictionary)
-  }
-}
-
-extension NSData: SyncbaseJsonConvertible {
-  public func toSyncbaseJson() throws -> (NSData, JsonDataType) {
-    return (self, JsonDataType.RawJson)
+extension NSData: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return self
   }
 }
diff --git a/SyncbaseCore/Source/Permissions.swift b/SyncbaseCore/Source/Permissions.swift
index beb1e34..1553bb7 100644
--- a/SyncbaseCore/Source/Permissions.swift
+++ b/SyncbaseCore/Source/Permissions.swift
@@ -55,14 +55,23 @@
   func toJsonable() -> [String: AnyObject] {
     return ["In": allowed, "NotIn": notAllowed]
   }
+
+  static func fromJsonable(jsonable: [String: AnyObject]) -> AccessList? {
+    guard let castIn = jsonable["In"] as? [String],
+      castNotIn = jsonable["NotIn"] as? [String] else {
+        return nil
+    }
+    return AccessList(
+      allowed: castIn as [BlessingPattern],
+      notAllowed: castNotIn as [BlessingPattern])
+  }
 }
 
 /// AccessController provides access control for various syncbase objects.
 public protocol AccessController {
+  /// getPermissions returns the current Permissions for an object.
+  func getPermissions() throws -> (Permissions, PermissionsVersion)
+
   /// setPermissions replaces the current Permissions for an object.
   func setPermissions(perms: Permissions, version: PermissionsVersion) throws
-
-  /// GetPermissions returns the current Permissions for an object.
-  /// For detailed documentation, see Object.GetPermissions.
-  func getPermissions() throws -> (Permissions, PermissionsVersion)
 }
diff --git a/SyncbaseCore/Source/Refs.swift b/SyncbaseCore/Source/Refs.swift
new file mode 100644
index 0000000..585fddb
--- /dev/null
+++ b/SyncbaseCore/Source/Refs.swift
@@ -0,0 +1,35 @@
+// 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.
+
+/// RefMap holds onto objs that get referenced by AsyncId between Go and Swift.
+/// The larger motivation is closures that get passed as function pointers have to be 'context-free'
+/// so we can't closure on a reference to a given future. By allowing the end user to hold onto
+/// this in various places (strongly typed to the appropriate T) then we can pass a handle
+/// back and forth safely.
+typealias AsyncId = Int32
+
+class RefMap<T>: Lockable {
+  private (set) var lastId: AsyncId = 0
+  private var refs = [AsyncId: T]()
+
+  /// Stores an object and returns the associated asyncId. If the object is already in the map
+  /// it will be stored twice -- ref does not actually perform reference counting.
+  func ref(obj: T) -> AsyncId {
+    let asyncId = OSAtomicIncrement32(&lastId)
+    lock { self.refs[asyncId] = obj }
+    return asyncId
+  }
+
+  /// Gets the associated value for a given asyncId.
+  func get(asyncId: AsyncId) -> T? {
+    return refs[asyncId]
+  }
+
+  /// Get and deletes any associated asyncId, returning the associated value.
+  func unref(asyncId: AsyncId) -> T? {
+    guard let p = refs[asyncId] else { return nil }
+    lock { self.refs[asyncId] = nil }
+    return p
+  }
+}
\ No newline at end of file
diff --git a/SyncbaseCore/Source/RowRange.swift b/SyncbaseCore/Source/RowRange.swift
index 39dd257..209a6da 100644
--- a/SyncbaseCore/Source/RowRange.swift
+++ b/SyncbaseCore/Source/RowRange.swift
@@ -12,15 +12,27 @@
   var limit: String { get }
 }
 
-/// StandardRowRange represents all rows with keys in [start, limit).
+/// RowRangeAll represents all rows.
+public struct RowRangeAll: RowRange {
+  public var start: String {
+    return ""
+  }
+
+  public var limit: String {
+    return ""
+  }
+}
+
+/// RowRangeStandard represents all rows with keys in [start, limit).
 /// If limit is "", all rows with keys >= start are included.
-public struct StandardRowRange: RowRange {
+public struct RowRangeStandard: RowRange {
   public let start: String
   /// If limit is "", all rows with keys >= start are included.
   public let limit: String
 }
 
-public struct SingleRow: RowRange {
+/// RowRangeSingleRow represents a single row with an explicit key.
+public struct RowRangeSingleRow: RowRange {
   public let row: String
 
   public var start: String {
@@ -32,8 +44,8 @@
   }
 }
 
-/// PrefixRange represents all rows with keys that have some prefix.
-public struct PrefixRange: RowRange {
+/// RowRangePrefix represents all rows with keys that have some prefix.
+public struct RowRangePrefix: RowRange {
   public let prefix: String
 
   /// Returns the start of the row range for the given prefix.
diff --git a/SyncbaseCore/Source/Stream.swift b/SyncbaseCore/Source/Stream.swift
index 544b3b0..ebf71cb 100644
--- a/SyncbaseCore/Source/Stream.swift
+++ b/SyncbaseCore/Source/Stream.swift
@@ -5,10 +5,10 @@
 import Foundation
 
 /// Base protocol for iterating through elements of unknown length.
-public protocol Stream: GeneratorType {
+public protocol Stream: SequenceType, GeneratorType {
   /// Err returns a non-nil error iff the stream encountered any errors. Err does
   /// not block.
-  func err() -> SyncbaseError?
+  func err() -> ErrorType?
 
   /// Cancel notifies the stream provider that it can stop producing elements.
   /// The client must call Cancel if it does not iterate through all elements
@@ -19,14 +19,14 @@
 }
 
 /// Typed-stream backed by anonymous callbacks
-public struct AnonymousStream<T>: Stream {
+public class AnonymousStream<T>: Stream {
   public typealias Element = T
-  public typealias FetchNextFunction = Void -> (T?, SyncbaseError?)
+  public typealias FetchNextFunction = Void -> (T?, ErrorType?)
   let fetchNextFunction: FetchNextFunction
   public typealias CancelFunction = Void -> Void
   let cancelFunction: CancelFunction
-  private var lastErr: SyncbaseError?
-  private var isCancelled: Bool = false
+  private var lastErr: ErrorType?
+  private var isDone: Bool = false
   init(fetchNextFunction: FetchNextFunction, cancelFunction: CancelFunction) {
     self.fetchNextFunction = fetchNextFunction
     self.cancelFunction = cancelFunction
@@ -40,8 +40,8 @@
   /// has returned `nil`.  Specific implementations of this protocol
   /// are encouraged to respond to violations of this requirement by
   /// calling `preconditionFailure("...")`.
-  public mutating func next() -> T? {
-    guard !isCancelled else {
+  public func next() -> T? {
+    guard !isDone else {
       return nil
     }
     let (result, err) = fetchNextFunction()
@@ -49,12 +49,13 @@
       return ret
     }
     lastErr = err
+    isDone = true
     return nil
   }
 
   /// Err returns a non-nil error iff the stream encountered any errors. Err does
   /// not block.
-  public func err() -> SyncbaseError? {
+  public func err() -> ErrorType? {
     return lastErr
   }
 
@@ -63,10 +64,10 @@
   /// (i.e. until Advance returns false). Cancel is idempotent and can be called
   /// concurrently with a goroutine that is iterating via Advance.
   /// Cancel causes Advance to subsequently return false. Cancel does not block.
-  public mutating func cancel() {
-    if !isCancelled {
+  public func cancel() {
+    if !isDone {
       cancelFunction()
-      isCancelled = true
+      isDone = true
     }
   }
 }
diff --git a/SyncbaseCore/Source/Syncbase.swift b/SyncbaseCore/Source/Syncbase.swift
index 877a994..9ffe8bd 100644
--- a/SyncbaseCore/Source/Syncbase.swift
+++ b/SyncbaseCore/Source/Syncbase.swift
@@ -15,7 +15,7 @@
   /// Private constructor -- because this class is a singleton it should only be called once
   /// and by the static instance method.
   private init() {
-    v23_syncbase_Init()
+    v23_syncbase_Init(v23_syncbase_Bool(false))
   }
 
   /// Create a database using the relative name and user's blessings.
@@ -25,20 +25,17 @@
 
   /// DatabaseForId returns the Database with the given app blessing and name (from the Id struct).
   public func database(databaseId: Identifier) throws -> Database {
-    guard let db = Database(databaseId: databaseId, batchHandle: nil) else {
-      throw SyncbaseError.InvalidUTF8(invalidUtf8: "\(databaseId)")
-    }
-    return db
+    return try Database(databaseId: databaseId, batchHandle: nil)
   }
 
   /// ListDatabases returns a list of all Database ids that the caller is allowed to see.
   /// The list is sorted by blessing, then by name.
   public func listDatabases() throws -> [Identifier] {
     var ids = v23_syncbase_Ids()
-    return try VError.maybeThrow({ err in
+    return try VError.maybeThrow { err in
       v23_syncbase_ServiceListDatabases(&ids, err)
       return ids.toIdentifiers()
-    })
+    }
   }
 
   /// Must return true before any Syncbase operation can work. Authorize using GoogleCredentials
@@ -81,14 +78,26 @@
 }
 
 extension Syncbase: AccessController {
-  /// setPermissions replaces the current Permissions for an object.
-  public func setPermissions(perms: Permissions, version: PermissionsVersion) throws {
-    preconditionFailure("Implement me")
+  public func getPermissions() throws -> (Permissions, PermissionsVersion) {
+    var cPermissions = v23_syncbase_Permissions()
+    var cVersion = v23_syncbase_String()
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_ServiceGetPermissions(
+        &cPermissions,
+        &cVersion,
+        errPtr)
+    }
+    // TODO(zinman): Verify that permissions defaulting to zero-value is correct for Permissions.
+    // We force cast of cVersion because we know it can be UTF8 converted.
+    return (try cPermissions.toPermissions() ?? Permissions(), cVersion.toString()!)
   }
 
-  /// getPermissions returns the current Permissions for an object.
-  /// For detailed documentation, see Object.GetPermissions.
-  public func getPermissions() throws -> (Permissions, PermissionsVersion) {
-    preconditionFailure("Implement me")
+  public func setPermissions(permissions: Permissions, version: PermissionsVersion) throws {
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_ServiceSetPermissions(
+        try v23_syncbase_Permissions(permissions),
+        try version.toCgoString(),
+        errPtr)
+    }
   }
 }
diff --git a/SyncbaseCore/Source/Watch.swift b/SyncbaseCore/Source/Watch.swift
index 2d5cb03..80b1c9c 100644
--- a/SyncbaseCore/Source/Watch.swift
+++ b/SyncbaseCore/Source/Watch.swift
@@ -5,7 +5,7 @@
 import Foundation
 
 public struct ResumeMarker {
-  let data: [UInt8]
+  let data: NSData
 }
 
 public struct WatchChange {
diff --git a/SyncbaseCore/Source/util/NSNumber.swift b/SyncbaseCore/Source/util/NSNumber.swift
deleted file mode 100644
index 7a1ec26..0000000
--- a/SyncbaseCore/Source/util/NSNumber.swift
+++ /dev/null
@@ -1,123 +0,0 @@
-// 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.
-
-import Foundation
-import CoreGraphics
-
-extension NSNumber {
-  /// Returns true if value can be cast to NSNumber or NSNumber?
-  static func isNSNumber<V>(value: V) -> Bool {
-    // Can't do this:
-    // if value is NSNumber {
-    // because it will always succeed through type coercion, even if value is an Int.
-    // Instead we use the dynamic (runtime) type
-    let type = value.dynamicType
-    return type is NSNumber.Type || type is NSNumber?.Type
-  }
-
-  /// Returns true if target can be set/cast from this NSNumber without precision-loss or type
-  /// conversion. For example ```NSNumber.init(bool: true) as Float``` will return a Swift Float of
-  /// value 1, where as this function would return false as the types are unrelated -- only
-  /// casting as Bool would return true. This function checks for 32/64-bit correctness with types.
-  ///
-  /// Caveat: Currently this function makes no attempt to determine signed/unsigned correctness of
-  /// the underlying data, although this is sometimes knowable with NSNumber.objCType.
-  func isTargetCastable<T>(inout target: T?) -> Bool {
-    // Allow matching types of this size, and bigger. Signed and unsigned of same size are not allowed.
-    // Swift: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html
-    // C types: https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaTouch64BitGuide/Major64-BitChanges/Major64-BitChanges.html
-    switch CFNumberGetType(self as CFNumberRef) {
-      // Obj-C bool is stored as Char, but if it's a @(YES) or @(NO) they are all a shared instance.
-    case .CharType where (unsafeAddressOf(self) == unsafeAddressOf(kCFBooleanFalse) ||
-      unsafeAddressOf(self) == unsafeAddressOf(kCFBooleanTrue)):
-      // We know we have a bool... make sure it's compatible
-      let type = target.dynamicType
-      guard type == Bool?.self || type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
-        return false
-      }
-      // Handle fixed lengths on 32/64 bit
-    case .SInt8Type, .CharType:
-      let type = target.dynamicType
-      guard type == Int?.self || type == UInt?.self ||
-      type == Int8?.self || type == Int16?.self || type == Int32?.self || type == Int64?.self ||
-      type == UInt8?.self || type == UInt16?.self || type == UInt32?.self || type == UInt64?.self ||
-      type == CChar?.self || type == CShort?.self || type == CInt?.self || type == CLong?.self || type == CLongLong?.self ||
-      type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
-        return false
-      }
-    case .SInt16Type, .ShortType:
-      let type = target.dynamicType
-      guard type == Int?.self || type == UInt?.self ||
-      type == Int16?.self || type == Int32?.self || type == Int64?.self ||
-      type == UInt16?.self || type == UInt32?.self || type == UInt64?.self ||
-      type == CShort?.self || type == CInt?.self || type == CLong?.self || type == CLongLong?.self ||
-      type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
-        return false
-      }
-    case .SInt32Type, .IntType:
-      let type = target.dynamicType
-      guard type == Int?.self || type == UInt?.self ||
-      type == Int32?.self || type == Int64?.self ||
-      type == UInt32?.self || type == UInt64?.self ||
-      type == CInt?.self || type == CLong?.self || type == CLongLong?.self ||
-      type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
-        return false
-      }
-    case .SInt64Type, .LongLongType:
-      let type = target.dynamicType
-      guard (type == Int?.self && sizeof(Int) == sizeof(CLongLong)) ||
-      (type == UInt?.self && sizeof(UInt) == sizeof(CLongLong)) ||
-      type == Int64?.self || type == UInt64?.self ||
-      type == CLongLong?.self ||
-      type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
-        return false
-      }
-    case .Float32Type, .FloatType:
-      let type = target.dynamicType
-      guard type == Float?.self ||
-      type == Double?.self ||
-      type == Float32?.self ||
-      type == Float64?.self ||
-      type == CFloat?.self || type == CDouble?.self || type == CGFloat?.self ||
-      type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
-        return false
-      }
-    case .Float64Type, .DoubleType: /* 64-bit IEEE 754 */
-      let type = target.dynamicType
-      guard type == Double?.self ||
-      type == Float64?.self ||
-      type == CDouble?.self || type == CGFloat?.self ||
-      type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
-        return false
-      }
-
-      // Handle 32/64-bit types
-    case .LongType, .NSIntegerType:
-      let type = target.dynamicType
-      guard type == Int?.self || type == UInt?.self ||
-      (type == Int32?.self && sizeof(Int32) == sizeof(NSInteger)) ||
-      (type == UInt32?.self && sizeof(UInt32) == sizeof(NSInteger)) ||
-      type == Int64?.self || type == UInt64?.self ||
-      type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self ||
-      type == CLong?.self || type == CLongLong?.self else {
-        return false
-      }
-    case .CGFloatType:
-      let type = target.dynamicType
-      guard (type == Float?.self && sizeof(CGFloat) == sizeof(Float)) ||
-      type == Double?.self ||
-      (type == Float32?.self && sizeof(CGFloat) == sizeof(Float32)) ||
-      type == Float64?.self ||
-      type == NSNumber?.self || type == AnyObject?.self || type == NSObject?.self else {
-        return false
-      }
-      // Misc
-    case .CFIndexType:
-      guard target.dynamicType == CFIndex?.self else {
-        return false
-      }
-    }
-    return true
-  }
-}
diff --git a/SyncbaseCore/Source/util/Types.swift b/SyncbaseCore/Source/util/Types.swift
index 67e5e11..eea44c3 100644
--- a/SyncbaseCore/Source/util/Types.swift
+++ b/SyncbaseCore/Source/util/Types.swift
@@ -7,6 +7,18 @@
 
 import Foundation
 
+extension v23_syncbase_BatchOptions {
+  init (_ opts: BatchOptions?) throws {
+    guard let o = opts else {
+      self.hint = v23_syncbase_String()
+      self.readOnly = false
+      return
+    }
+    self.hint = try o.hint?.toCgoString() ?? v23_syncbase_String()
+    self.readOnly = o.readOnly
+  }
+}
+
 extension v23_syncbase_Bool {
   init(_ bool: Bool) {
     switch bool {
@@ -23,6 +35,97 @@
   }
 }
 
+extension v23_syncbase_Bytes {
+  init(_ data: NSData) {
+    let p = malloc(data.length)
+    if p == nil {
+      fatalError("Couldn't allocate \(data.length) bytes")
+    }
+    let n = data.length
+    data.getBytes(p, length: n)
+    self.p = UnsafeMutablePointer<UInt8>(p)
+    self.n = Int32(n)
+  }
+
+  // Return value takes ownership of the memory associated with this object.
+  func toNSData() -> NSData? {
+    if p == nil {
+      return nil
+    }
+    return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(p), length: Int(n), freeWhenDone: true)
+  }
+}
+
+extension v23_syncbase_Id {
+  init?(_ id: Identifier) {
+    do {
+      self.name = try id.name.toCgoString()
+      self.blessing = try id.blessing.toCgoString()
+    } catch (let e) {
+      log.warning("Unable to UTF8-encode id: \(e)")
+      return nil
+    }
+  }
+
+  func toIdentifier() -> Identifier? {
+    guard let name = name.toString(),
+      blessing = blessing.toString() else {
+        return nil
+    }
+    return Identifier(name: name, blessing: blessing)
+  }
+}
+
+extension v23_syncbase_Ids {
+  func toIdentifiers() -> [Identifier] {
+    var ids: [Identifier] = []
+    for i in 0 ..< n {
+      let idStruct = p.advancedBy(Int(i)).memory
+      if let id = idStruct.toIdentifier() {
+        ids.append(id)
+      }
+    }
+    if p != nil {
+      free(p)
+    }
+    return ids
+  }
+}
+
+extension v23_syncbase_Permissions {
+  init(_ permissions: Permissions?) throws {
+    guard let p = permissions where !p.isEmpty else {
+      // Zero-value constructor.
+      self.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)
+    self.json = bytes
+  }
+
+  func toPermissions() throws -> Permissions? {
+    guard let data = self.json.toNSData(),
+      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],
+        accessList = AccessList.fromJsonable(jsonAcessList) else {
+          throw SyncbaseError.CastError(obj: v)
+      }
+      p[key] = accessList
+    }
+    return p
+  }
+}
+
 extension v23_syncbase_String {
   init?(_ string: String) {
     // TODO: If possible, make one copy instead of two, e.g. using s.getCString.
@@ -111,27 +214,6 @@
   }
 }
 
-extension v23_syncbase_Bytes {
-  init(_ data: NSData) {
-    let p = malloc(data.length)
-    if p == nil {
-      fatalError("Couldn't allocate \(data.length) bytes")
-    }
-    let n = data.length
-    data.getBytes(p, length: n)
-    self.p = UnsafeMutablePointer<UInt8>(p)
-    self.n = Int32(n)
-  }
-
-  // Return value takes ownership of the memory associated with this object.
-  func toNSData() -> NSData? {
-    if p == nil {
-      return nil
-    }
-    return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(p), length: Int(n), freeWhenDone: true)
-  }
-}
-
 // Note, we don't define init?(VError) since we never pass Swift VError objects to Go.
 extension v23_syncbase_VError {
   // Return value takes ownership of the memory associated with this object.
@@ -142,7 +224,7 @@
     // Take ownership of all memory before checking optionals.
     let vId = id.toString(), vMsg = msg.toString(), vStack = stack.toString()
     // TODO: Stop requiring id, msg, and stack to be valid UTF8?
-    return VError(id: vId!, actionCode: actionCode, msg: vMsg!, stack: vStack!)
+    return VError(id: vId!, actionCode: actionCode, msg: vMsg ?? "", stack: vStack!)
   }
 }
 
@@ -156,66 +238,13 @@
     var e = v23_syncbase_VError()
     let res = try f(&e)
     if let err = e.toVError() {
+      // We might be able to convert this VError into a SyncbaseError depending on the ID.
+      if let syncbaseError = SyncbaseError(err) {
+        throw syncbaseError
+      }
       throw err
     }
     return res
   }
 }
 
-extension v23_syncbase_Ids {
-  func toIdentifiers() -> [Identifier] {
-    var ids: [Identifier] = []
-    for i in 0 ..< n {
-      let idStruct = p.advancedBy(Int(i)).memory
-      if let id = idStruct.toIdentifier() {
-        ids.append(id)
-      }
-    }
-    if p != nil {
-      free(p)
-    }
-    return ids
-  }
-}
-
-extension v23_syncbase_Id {
-  init?(_ id: Identifier) {
-    do {
-      self.name = try id.name.toCgoString()
-      self.blessing = try id.blessing.toCgoString()
-    } catch (let e) {
-      log.warning("Unable to UTF8-encode id: \(e)")
-      return nil
-    }
-  }
-
-  func toIdentifier() -> Identifier? {
-    guard let name = name.toString(),
-      let blessing = blessing.toString() else {
-        return nil
-    }
-    return Identifier(name: name, blessing: blessing)
-  }
-}
-
-extension v23_syncbase_Permissions {
-  init?(_ permissions: Permissions?) {
-    guard let p = permissions where !p.isEmpty else {
-      // Zero-value constructor.
-      self.json = v23_syncbase_Bytes()
-      return
-    }
-    var m = [String: AnyObject]()
-    for (key, value) in p {
-      m[key as String] = (value as AccessList).toJsonable()
-    }
-    do {
-      let serialized = try NSJSONSerialization.serialize(m)
-      let bytes = v23_syncbase_Bytes(serialized)
-      self.json = bytes
-    } catch {
-      log.warning("Unable to serialize permissions: \(permissions)")
-      return nil
-    }
-  }
-}
diff --git a/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj b/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
index 0026a4f..7407901 100644
--- a/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
+++ b/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
@@ -25,14 +25,11 @@
 		30AD2E401CDD508700A28A0C /* Syncgroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2A1CDD508700A28A0C /* Syncgroup.swift */; };
 		30AD2E441CDD508700A28A0C /* Vom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2E1CDD508700A28A0C /* Vom.swift */; };
 		30AD2E451CDD508700A28A0C /* Watch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2F1CDD508700A28A0C /* Watch.swift */; };
-		30AD2E4D1CDD508D00A28A0C /* JSONTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E491CDD508D00A28A0C /* JSONTests.swift */; };
-		30AD2E4E1CDD508D00A28A0C /* NSNumberTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E4A1CDD508D00A28A0C /* NSNumberTests.swift */; };
 		30AD2E801CDD569200A28A0C /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E791CDD569200A28A0C /* Logging.swift */; };
 		30AD2E811CDD569200A28A0C /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E7A1CDD569200A28A0C /* Strings.swift */; };
 		30AD2E821CDD569200A28A0C /* Threads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E7B1CDD569200A28A0C /* Threads.swift */; };
 		30AD2E891CDD56B700A28A0C /* Exceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30AD2E861CDD56B700A28A0C /* Exceptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		30AD2E8A1CDD56B700A28A0C /* Exceptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E871CDD56B700A28A0C /* Exceptions.m */; };
-		30AD2E8B1CDD56B700A28A0C /* NSNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E881CDD56B700A28A0C /* NSNumber.swift */; };
 		30AD2E911CDD593000A28A0C /* Principal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E901CDD593000A28A0C /* Principal.swift */; };
 		30AD2E931CDD60A600A28A0C /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E921CDD60A600A28A0C /* Types.swift */; };
 		30BCD5D01CEFA32900C76DA2 /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30BCD5CF1CEFA32900C76DA2 /* CoreBluetooth.framework */; };
@@ -40,6 +37,8 @@
 		30D45FCA1CE14A8A004A59FA /* go_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 30D45FC51CE14A8A004A59FA /* go_types.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		30D45FCD1CE14A8A004A59FA /* sbcore_arm64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 30D45FC81CE14A8A004A59FA /* sbcore_arm64.a */; };
 		930DFCE21CEE46DE00738DB8 /* OAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 930DFCE11CEE46DE00738DB8 /* OAuth.swift */; };
+		930DFCEB1CEED8BD00738DB8 /* Refs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 930DFCEA1CEED8BD00738DB8 /* Refs.swift */; };
+		930DFCF51CEED96B00738DB8 /* Locking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 930DFCF41CEED96B00738DB8 /* Locking.swift */; };
 		9351A4941CE46DB9009CC4F4 /* sbcore_amd64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9351A4931CE46DB9009CC4F4 /* sbcore_amd64.a */; };
 		93D3AD5C1CE4392A00A80CDA /* libleveldb_amd64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D3AD581CE4392A00A80CDA /* libleveldb_amd64.a */; };
 		93D3AD5D1CE4392A00A80CDA /* libleveldb_arm64.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D3AD591CE4392A00A80CDA /* libleveldb_arm64.a */; };
@@ -79,14 +78,11 @@
 		30AD2E2E1CDD508700A28A0C /* Vom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vom.swift; sourceTree = "<group>"; };
 		30AD2E2F1CDD508700A28A0C /* Watch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Watch.swift; sourceTree = "<group>"; };
 		30AD2E481CDD508D00A28A0C /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		30AD2E491CDD508D00A28A0C /* JSONTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONTests.swift; sourceTree = "<group>"; };
-		30AD2E4A1CDD508D00A28A0C /* NSNumberTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSNumberTests.swift; sourceTree = "<group>"; };
 		30AD2E791CDD569200A28A0C /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Logging.swift; path = util/Logging.swift; sourceTree = "<group>"; };
 		30AD2E7A1CDD569200A28A0C /* Strings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Strings.swift; path = util/Strings.swift; sourceTree = "<group>"; };
 		30AD2E7B1CDD569200A28A0C /* Threads.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Threads.swift; path = util/Threads.swift; sourceTree = "<group>"; };
 		30AD2E861CDD56B700A28A0C /* Exceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exceptions.h; path = util/Exceptions.h; sourceTree = "<group>"; };
 		30AD2E871CDD56B700A28A0C /* Exceptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Exceptions.m; path = util/Exceptions.m; sourceTree = "<group>"; };
-		30AD2E881CDD56B700A28A0C /* NSNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSNumber.swift; path = util/NSNumber.swift; sourceTree = "<group>"; };
 		30AD2E901CDD593000A28A0C /* Principal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Principal.swift; sourceTree = "<group>"; };
 		30AD2E921CDD60A600A28A0C /* Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Types.swift; path = util/Types.swift; sourceTree = "<group>"; };
 		30BCD5CF1CEFA32900C76DA2 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; };
@@ -94,6 +90,8 @@
 		30D45FC51CE14A8A004A59FA /* go_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = go_types.h; sourceTree = "<group>"; };
 		30D45FC81CE14A8A004A59FA /* sbcore_arm64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = sbcore_arm64.a; sourceTree = "<group>"; };
 		930DFCE11CEE46DE00738DB8 /* OAuth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OAuth.swift; sourceTree = "<group>"; };
+		930DFCEA1CEED8BD00738DB8 /* Refs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Refs.swift; sourceTree = "<group>"; };
+		930DFCF41CEED96B00738DB8 /* Locking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locking.swift; sourceTree = "<group>"; };
 		9351A4931CE46DB9009CC4F4 /* sbcore_amd64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = sbcore_amd64.a; sourceTree = "<group>"; };
 		93D3AD581CE4392A00A80CDA /* libleveldb_amd64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libleveldb_amd64.a; sourceTree = "<group>"; };
 		93D3AD591CE4392A00A80CDA /* libleveldb_arm64.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libleveldb_arm64.a; sourceTree = "<group>"; };
@@ -150,13 +148,14 @@
 		30AD2E191CDD508700A28A0C /* Source */ = {
 			isa = PBXGroup;
 			children = (
+				30AD2E281CDD508700A28A0C /* SyncbaseCore.h */,
+				30AD2E211CDD508700A28A0C /* Info.plist */,
 				30AD2E1B1CDD508700A28A0C /* Batch.swift */,
 				30AD2E1C1CDD508700A28A0C /* Blob.swift */,
 				30AD2E1D1CDD508700A28A0C /* Collection.swift */,
 				30AD2E1E1CDD508700A28A0C /* Database.swift */,
 				30AD2E1F1CDD508700A28A0C /* Errors.swift */,
 				30AD2E201CDD508700A28A0C /* Identifier.swift */,
-				30AD2E211CDD508700A28A0C /* Info.plist */,
 				30AD2E221CDD508700A28A0C /* Marshal.swift */,
 				930DFCE11CEE46DE00738DB8 /* OAuth.swift */,
 				30AD2E231CDD508700A28A0C /* Permissions.swift */,
@@ -165,11 +164,10 @@
 				30AD2E261CDD508700A28A0C /* Service.swift */,
 				30AD2E271CDD508700A28A0C /* Stream.swift */,
 				30AD2E291CDD508700A28A0C /* Syncbase.swift */,
-				30AD2E281CDD508700A28A0C /* SyncbaseCore.h */,
 				30AD2E2A1CDD508700A28A0C /* Syncgroup.swift */,
-				30AD2E771CDD567D00A28A0C /* Util */,
 				30AD2E2E1CDD508700A28A0C /* Vom.swift */,
 				30AD2E2F1CDD508700A28A0C /* Watch.swift */,
+				30AD2E771CDD567D00A28A0C /* Util */,
 			);
 			path = Source;
 			sourceTree = "<group>";
@@ -178,8 +176,6 @@
 			isa = PBXGroup;
 			children = (
 				30AD2E481CDD508D00A28A0C /* Info.plist */,
-				30AD2E491CDD508D00A28A0C /* JSONTests.swift */,
-				30AD2E4A1CDD508D00A28A0C /* NSNumberTests.swift */,
 				30A1F52D1CE68465008FC205 /* BasicDatabaseTests.swift */,
 			);
 			path = Tests;
@@ -205,8 +201,9 @@
 			children = (
 				30AD2E861CDD56B700A28A0C /* Exceptions.h */,
 				30AD2E871CDD56B700A28A0C /* Exceptions.m */,
-				30AD2E881CDD56B700A28A0C /* NSNumber.swift */,
+				930DFCF41CEED96B00738DB8 /* Locking.swift */,
 				30AD2E791CDD569200A28A0C /* Logging.swift */,
+				930DFCEA1CEED8BD00738DB8 /* Refs.swift */,
 				30AD2E7A1CDD569200A28A0C /* Strings.swift */,
 				30AD2E7B1CDD569200A28A0C /* Threads.swift */,
 				30AD2E921CDD60A600A28A0C /* Types.swift */,
@@ -342,9 +339,10 @@
 				30AD2E3F1CDD508700A28A0C /* Syncbase.swift in Sources */,
 				30AD2E401CDD508700A28A0C /* Syncgroup.swift in Sources */,
 				30AD2E811CDD569200A28A0C /* Strings.swift in Sources */,
-				30AD2E8B1CDD56B700A28A0C /* NSNumber.swift in Sources */,
+				930DFCF51CEED96B00738DB8 /* Locking.swift in Sources */,
 				30AD2E451CDD508700A28A0C /* Watch.swift in Sources */,
 				30AD2E821CDD569200A28A0C /* Threads.swift in Sources */,
+				930DFCEB1CEED8BD00738DB8 /* Refs.swift in Sources */,
 				30AD2E801CDD569200A28A0C /* Logging.swift in Sources */,
 				30AD2E351CDD508700A28A0C /* Errors.swift in Sources */,
 				930DFCE21CEE46DE00738DB8 /* OAuth.swift in Sources */,
@@ -364,8 +362,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				30A1F52E1CE68465008FC205 /* BasicDatabaseTests.swift in Sources */,
-				30AD2E4D1CDD508D00A28A0C /* JSONTests.swift in Sources */,
-				30AD2E4E1CDD508D00A28A0C /* NSNumberTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/SyncbaseCore/Tests/BasicDatabaseTests.swift b/SyncbaseCore/Tests/BasicDatabaseTests.swift
index 095c9fb..87a5626 100644
--- a/SyncbaseCore/Tests/BasicDatabaseTests.swift
+++ b/SyncbaseCore/Tests/BasicDatabaseTests.swift
@@ -11,19 +11,38 @@
   // MARK: Basic test helpers
 
   func withTestDb(runBlock: Database throws -> Void) {
-    do {
-      let db = try Syncbase.instance.database("test1")
-      print("Got db \(db)")
-      XCTAssertFalse(try db.exists(), "Database shouldn't exist before being created")
-      print("Creating db \(db)")
-      try db.create(nil)
-      XCTAssertTrue(try db.exists(), "Database should exist after being created")
-
+    withTestDbAsync { (db, cleanup) in
+      defer { cleanup() }
       try runBlock(db)
+    }
+  }
 
-      print("Destroying db \(db)")
-      try db.destroy()
-      XCTAssertFalse(try db.exists(), "Database shouldn't exist after being destroyed")
+  func withTestDbAsync(runBlock: (db: Database, cleanup: Void -> Void) throws -> Void) {
+    do {
+      // Randomize the name to prevent conflicts between tests
+      let dbName = "test\(NSUUID().UUIDString)".stringByReplacingOccurrencesOfString("-", withString: "")
+      let db = try Syncbase.instance.database(dbName)
+      let cleanup = {
+        do {
+          print("Destroying db \(db)")
+          try db.destroy()
+          XCTAssertFalse(try db.exists(), "Database shouldn't exist after being destroyed")
+        } catch let e {
+          log.warning("Unable to delete db: \(e)")
+        }
+      }
+      do {
+        print("Got db \(db)")
+        XCTAssertFalse(try db.exists(), "Database shouldn't exist before being created")
+        print("Creating db \(db)")
+        try db.create(nil)
+        XCTAssertTrue(try db.exists(), "Database should exist after being created")
+        // Always delete the db at the end to prevent conflicts between tests
+        try runBlock(db: db, cleanup: cleanup)
+      } catch let e {
+        XCTFail("Got unexpected exception: \(e)")
+        cleanup()
+      }
     } catch (let e) {
       XCTFail("Got unexpected exception: \(e)")
     }
@@ -50,8 +69,10 @@
   func testListDatabases() {
     withTestDb { db in
       let databases = try Syncbase.instance.listDatabases()
-      XCTAssertEqual(databases.count, 1)
-      XCTAssertTrue(databases.first! == db.databaseId)
+      XCTAssertFalse(databases.isEmpty)
+      if !databases.isEmpty {
+        XCTAssertTrue(databases.first! == db.databaseId)
+      }
     }
   }
 
@@ -67,11 +88,11 @@
     }
   }
 
-  // MARK: Test getting/putting data
+  // MARK: Test getting/putting/deleting data
 
-  class func testGetPutRow<T: SyncbaseJsonConvertible where T: Equatable>(collection: Collection, key: String, targetValue: T, equals: ((T, T) -> Bool)? = nil) throws {
+  class func testGetPutRow<T: SyncbaseConvertible where T: Equatable>(collection: Collection, key: String, targetValue: T, equals: ((T, T) -> Bool)? = nil) throws {
     var value: T? = try collection.get(key)
-    XCTAssertNil(value, "Row shouldn't exist yet")
+    XCTAssertNil(value, "Row shouldn't exist yet; yet it has value \(value)")
     try collection.put(key, value: targetValue)
     value = try collection.get(key)
     if let eq = equals {
@@ -84,73 +105,219 @@
     XCTAssertNil(value, "Deleted row shouldn't exist")
   }
 
-  func testPrimitivesGetPut() {
+  func testRawBytesGetPut() {
     withTestCollection { db, collection in
       let key = "testrow"
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: true)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: false)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int8.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int16.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int32.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int64.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Int.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt8.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt16.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt32.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt64.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: UInt.max)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: Float(M_PI), equals: BasicDatabaseTests.floatEq)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: M_PI, equals: BasicDatabaseTests.doubleEq)
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: "oh hai")
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: "你好,世界 👠💪🏿")
-    }
-  }
-
-  func testNonMixedArrayGetPut() {
-    let key = "testrow"
-    withTestCollection { db, collection in
-      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: [])
-      try BasicDatabaseTests.testGetPutRow(collection, key: key,
-        targetValue: [UInt.max, UInt.min])
-      try BasicDatabaseTests.testGetPutRow(collection, key: key,
-        targetValue: [Int.max, Int.min])
-      try BasicDatabaseTests.testGetPutRow(collection, key: key,
-        targetValue: ["oh hai", "你好,世界 👠💪🏿"])
-    }
-  }
-
-  func testMixedArrayGetPut() {
-    let key = "testrow"
-    withTestCollection { db, collection in
-      try BasicDatabaseTests.testGetPutRow(collection, key: key,
-        targetValue: [15, UInt.max, Int.min])
-      try BasicDatabaseTests.testGetPutRow(collection, key: key,
-        targetValue: [false, 3939, "你好,世界 👠💪🏿"])
-    }
-  }
-
-  func testIntGetPut() {
-    withTestCollection { db, collection in
-      let key = "testrow"
-      let targetValue = 283783
-      var value: Int? = try collection.get(key)
-      XCTAssertNil(value, "Row shouldn't exist yet")
-      try collection.put(key, value: targetValue)
-      value = try collection.get(key)
-      XCTAssertEqual(value!, targetValue, "Value should be defined and \(targetValue)")
       try collection.delete(key)
-      value = try collection.get(key)
-      XCTAssertNil(value, "Deleted row shouldn't exist")
+      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: NSData())
+      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: NSJSONSerialization.hackSerializeAnyObject(false))
+      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: NSJSONSerialization.hackSerializeAnyObject(M_PI))
+      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: NSJSONSerialization.hackSerializeAnyObject("你好,世界 👠💪🏿"))
+      try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: "\0\0\0".dataUsingEncoding(NSUTF8StringEncoding)!)
+      if let p = NSBundle.mainBundle().executablePath,
+        data = NSFileManager.defaultManager().contentsAtPath(p) {
+          try BasicDatabaseTests.testGetPutRow(collection, key: key, targetValue: data)
+      }
     }
   }
 
-  // MARK: Helpers
+  func testDeleteRange() {
+    withTestCollection { db, collection in
+      // Generate some test data (1 to 4096 in hex)
+      var data = [String: NSData]()
+      for i in 1...4096 {
+        let key = NSString(format: "%x", i) as String
+        let value = key.dataUsingEncoding(NSUTF8StringEncoding)!
+        data[key] = value
+        try collection.put(key, value: value)
+      }
 
-  class func floatEq(lhs: Float, rhs: Float) -> Bool {
-    return fabs(lhs - rhs) <= FLT_EPSILON
+      // Delete single row
+      try collection.deleteRange(RowRangeSingleRow(row: "9"))
+      let value: NSData? = try collection.get("9")
+      XCTAssertNil(value)
+
+      // Delete a*
+      try collection.deleteRange(RowRangePrefix(prefix: "a"))
+      var stream = try collection.scan(RowRangePrefix(prefix: "a"))
+      XCTAssertNil(stream.next())
+
+      // Delete b-bc
+      try collection.deleteRange(RowRangeStandard(start: "b", limit: "bc"))
+      // Get all the keys including bc and after
+      var keys = Array(data.keys.filter { $0.hasPrefix("b") }).sort()
+      let bcIdx = keys.indexOf("bc")!
+      keys = Array(keys.dropFirst(bcIdx))
+      // Verify that's what's in the db
+      stream = try collection.scan(RowRangePrefix(prefix: "b"))
+      for (key, _) in stream {
+        let targetKey = keys[0]
+        XCTAssertEqual(key, targetKey)
+        keys.removeFirst()
+      }
+      XCTAssertTrue(keys.isEmpty)
+      XCTAssertNil(stream.next())
+    }
   }
 
-  class func doubleEq(lhs: Double, rhs: Double) -> Bool {
-    return fabs(lhs - rhs) <= DBL_EPSILON
+  func testScan() {
+    withTestCollection { db, collection in
+      // Generate some test data (1 to 200 in hex)
+      var data = [String: NSData]()
+      for i in 1...200 {
+        let key = NSString(format: "%x", i) as String
+        let value = key.dataUsingEncoding(NSUTF8StringEncoding)!
+        data[key] = value
+        try collection.put(key, value: value)
+      }
+
+      // All rows
+      var stream = try collection.scan(RowRangeAll())
+      var keys = Array(data.keys).sort() // lexographic sort
+      for (key, getValue) in stream {
+        let value = try getValue() as! NSData
+        let valueStr = NSString(data: value, encoding: NSUTF8StringEncoding)!
+        XCTAssertEqual(key, valueStr)
+
+        let targetKey = keys[0]
+        XCTAssertEqual(key, targetKey)
+        keys.removeFirst()
+      }
+      XCTAssertTrue(keys.isEmpty)
+      XCTAssertNil(stream.next())
+
+      // Single Row
+      stream = try collection.scan(RowRangeSingleRow(row: "a"))
+      guard let (key, getValue) = stream.next() else {
+        XCTFail()
+        return
+      }
+      XCTAssertEqual(key, "a")
+      let value = try getValue() as! NSData
+      let valueStr = NSString(data: value, encoding: NSUTF8StringEncoding)!
+      XCTAssertEqual(key, valueStr)
+      XCTAssertNil(stream.next())
+      // Doing it again should be ok
+      XCTAssertNil(stream.next())
+
+      // Prefix
+      stream = try collection.scan(RowRangePrefix(prefix: "8"))
+      keys = Array(data.keys.filter { $0.hasPrefix("8") }).sort() // lexographic sort
+      for (key, _) in stream {
+        let targetKey = keys[0]
+        XCTAssertEqual(key, targetKey)
+        keys.removeFirst()
+      }
+      XCTAssertTrue(keys.isEmpty)
+      XCTAssertNil(stream.next())
+    }
+  }
+
+  // MARK: Test batches
+
+  func testBatchCommit() {
+    withTestDb { db in
+      let batchDb = try db.beginBatch(nil)
+      let collection = try batchDb.collection("collection2")
+      try collection.create(nil)
+      try collection.put("a", value: NSData())
+      try collection.put("1", value: NSData())
+      try collection.put("2", value: NSData())
+      try batchDb.commit()
+      do {
+        let _: NSData? = try collection.get("a")
+        XCTFail("Should have thrown an UnknownBatch exception")
+      } catch SyncbaseError.UnknownBatch {
+        // Expect this to fail since the batch is already commited -- the collection reference
+        // is now invalid.
+      } catch {
+        XCTFail("Should have thrown an UnknownBatch exception")
+      }
+      let valueA: NSData? = try db.collection("collection2").get("a")
+      let value1: NSData? = try db.collection("collection2").get("1")
+      let value2: NSData? = try db.collection("collection2").get("2")
+      XCTAssertNotNil(valueA)
+      XCTAssertNotNil(value1)
+      XCTAssertNotNil(value2)
+    }
+  }
+
+  func testBatchAbort() {
+    withTestDb { db in
+      let batchDb = try db.beginBatch(nil)
+      let collection = try batchDb.collection("collection2")
+      try collection.create(nil)
+      try collection.put("b", value: NSData())
+      try collection.put("c", value: NSData())
+      try batchDb.abort()
+      do {
+        let _: NSData? = try collection.get("a")
+        XCTFail("Should have thrown an UnknownBatch exception")
+      } catch SyncbaseError.UnknownBatch {
+        // Expect this to fail since the batch is already commited -- the collection reference
+        // is now invalid.
+      } catch {
+        XCTFail("Should have thrown an UnknownBatch exception")
+      }
+      let valueB: NSData? = try db.collection("collection2").get("b")
+      let valueC: NSData? = try db.collection("collection2").get("c")
+      XCTAssertNil(valueB)
+      XCTAssertNil(valueC)
+    }
+  }
+
+  func testRunInBatchAutoCommit() {
+    let completed = expectationWithDescription("Completed runInBatch for auto commit")
+    withTestDbAsync { (db, cleanup) in
+      Batch.runInBatch(
+        db: db,
+        opts: nil,
+        op: { batchDb in
+          let collection = try batchDb.collection("collection3")
+          try collection.create(nil)
+          try collection.put("a", value: NSData())
+        },
+        completionHandler: { err in
+          XCTAssertNil(err)
+          do {
+            let collection = try db.collection("collection3")
+            let value: NSData? = try collection.get("a")
+            XCTAssertNotNil(value)
+          } catch let e {
+            XCTFail("Unexpected error: \(e)")
+          }
+          cleanup()
+          completed.fulfill()
+      })
+    }
+    waitForExpectationsWithTimeout(2) { XCTAssertNil($0) }
+  }
+
+  func testRunInBatchAbort() {
+    let completed = expectationWithDescription("Completed runInBatch for abort")
+    withTestDbAsync { (db, cleanup) in
+      Batch.runInBatch(
+        db: db,
+        opts: nil,
+        op: { batchDb in
+          let collection = try batchDb.collection("collection4")
+          try collection.create(nil)
+          try collection.put("a", value: NSData())
+          try batchDb.abort()
+        },
+        completionHandler: { err in
+          XCTAssertNil(err)
+          do {
+            let collection = try db.collection("collection4")
+            let value: NSData? = try collection.get("a")
+            XCTAssertNil(value)
+          } catch let e {
+            XCTFail("Unexpected error: \(e)")
+          }
+          cleanup()
+          completed.fulfill()
+      })
+    }
+    waitForExpectationsWithTimeout(2) { XCTAssertNil($0) }
   }
 }
diff --git a/SyncbaseCore/Tests/JsonTests.swift b/SyncbaseCore/Tests/JsonTests.swift
deleted file mode 100644
index 0a91288..0000000
--- a/SyncbaseCore/Tests/JsonTests.swift
+++ /dev/null
@@ -1,77 +0,0 @@
-// 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.
-
-import XCTest
-@testable import SyncbaseCore
-
-class JSONTests: XCTestCase {
-  // Emulate syncbase's put
-  func toJSON(any: SyncbaseJsonConvertible) -> (NSData, JsonDataType) {
-    return try! any.toSyncbaseJson()
-  }
-
-  func toString(json: NSData) -> String {
-    return String(data: json, encoding: NSUTF8StringEncoding)!
-  }
-
-  func testBasicEncoding() {
-    var (data, type) = try! 5.toSyncbaseJson()
-    XCTAssertEqual(toString(data), "5")
-    XCTAssertEqual(type, JsonDataType.Int)
-    (data, type) = toJSON(5)
-    XCTAssertEqual(toString(data), "5")
-    XCTAssertEqual(type, JsonDataType.Int)
-    (data, type) = try! true.toSyncbaseJson()
-    XCTAssertEqual(toString(data), "true")
-    XCTAssertEqual(type, JsonDataType.Bool)
-    (data, type) = try! false.toSyncbaseJson()
-    XCTAssertEqual(toString(data), "false")
-    XCTAssertEqual(type, JsonDataType.Bool)
-    (data, type) = try! Int8(5).toSyncbaseJson()
-    XCTAssertEqual(toString(data), "5")
-    XCTAssertEqual(type, JsonDataType.Int8)
-    (data, type) = try! Int16(5).toSyncbaseJson()
-    XCTAssertEqual(toString(data), "5")
-    XCTAssertEqual(type, JsonDataType.Int16)
-    (data, type) = try! Int32(5).toSyncbaseJson()
-    XCTAssertEqual(toString(data), "5")
-    XCTAssertEqual(type, JsonDataType.Int32)
-    (data, type) = try! Int64(5).toSyncbaseJson()
-    XCTAssertEqual(toString(data), "5")
-    XCTAssertEqual(type, JsonDataType.Int64)
-    (data, type) = try! Int64(Int64(Int32.max) + 10).toSyncbaseJson()
-    XCTAssertEqual(toString(data), "\(Int64(Int32.max) + 10)")
-    XCTAssertEqual(type, JsonDataType.Int64)
-    (data, type) = try! Float(5.0).toSyncbaseJson()
-    XCTAssertEqual(toString(data), "5") // 5 is ok here since we pass knowledge it's a float
-    XCTAssertEqual(type, JsonDataType.Float)
-    (data, type) = try! Double(5.1).toSyncbaseJson()
-    XCTAssertEqual(toString(data), "5.1") // 5 is ok here since we pass knowledge it's a float
-    XCTAssertEqual(type, JsonDataType.Double)
-    (data, type) = try! "Hello world! 👠".toSyncbaseJson()
-    XCTAssertEqual(toString(data), "\"Hello world! 👠\"")
-    XCTAssertEqual(type, JsonDataType.String)
-    (data, type) = try! [1, 2, 3, 4].toSyncbaseJson()
-    XCTAssertEqual(toString(data), "[1,2,3,4]")
-    XCTAssertEqual(type, JsonDataType.Array)
-    (data, type) = toJSON([1, 2, 3, 4])
-    XCTAssertEqual(toString(data), "[1,2,3,4]")
-    XCTAssertEqual(type, JsonDataType.Array)
-    (data, type) = try! ["a", "b", "c"].toSyncbaseJson()
-    XCTAssertEqual(toString(data), "[\"a\",\"b\",\"c\"]")
-    XCTAssertEqual(type, JsonDataType.Array)
-    (data, type) = try! ["a": 1].toSyncbaseJson()
-    XCTAssertEqual(toString(data), "{\"a\":1}")
-    XCTAssertEqual(type, JsonDataType.Dictionary)
-    (data, type) = try! ["b": true].toSyncbaseJson()
-    XCTAssertEqual(toString(data), "{\"b\":true}")
-    XCTAssertEqual(type, JsonDataType.Dictionary)
-    (data, type) = try! ["c": "👠"].toSyncbaseJson()
-    XCTAssertEqual(toString(data), "{\"c\":\"👠\"}")
-    XCTAssertEqual(type, JsonDataType.Dictionary)
-    (data, type) = toJSON(["c": "👠"])
-    XCTAssertEqual(toString(data), "{\"c\":\"👠\"}")
-    XCTAssertEqual(type, JsonDataType.Dictionary)
-  }
-}
diff --git a/SyncbaseCore/Tests/NSNumberTests.swift b/SyncbaseCore/Tests/NSNumberTests.swift
deleted file mode 100644
index 110e2ee..0000000
--- a/SyncbaseCore/Tests/NSNumberTests.swift
+++ /dev/null
@@ -1,144 +0,0 @@
-// 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.
-
-import XCTest
-@testable import SyncbaseCore
-
-class NSNumberTests: XCTestCase {
-  func testISNSNumber() {
-    XCTAssertTrue(NSNumber.isNSNumber(NSNumber(bool: true)))
-    XCTAssertFalse(NSNumber.isNSNumber(true))
-    XCTAssertTrue(NSNumber.isNSNumber(true as AnyObject))
-    XCTAssertTrue(NSNumber.isNSNumber(true as NSNumber))
-    XCTAssertFalse(NSNumber.isNSNumber(""))
-    XCTAssertFalse(NSNumber.isNSNumber(5))
-    XCTAssertTrue(NSNumber.isNSNumber(5 as AnyObject))
-  }
-
-  func testConservationOfPrecision() {
-    var anyobj: AnyObject?
-    var nsnumber: NSNumber?
-    var bool: Bool? = true
-    var int: Int? = Int.max
-    var int8: Int8? = Int8.max
-    var int16: Int16? = Int16.max
-    var int32: Int32? = Int32.max
-    var int64: Int64? = Int64.max
-    var float: Float? = Float(Int32.max)
-    var double: Double? = Double(Int64.max)
-    var long: CLong? = CLong.max
-    var uint: UInt? = UInt.max
-    var uint8: UInt8? = UInt8.max
-    var uint16: UInt16? = UInt16.max
-    var uint32: UInt32? = UInt32.max
-    var uint64: UInt64? = UInt64.max
-    var ulong: CUnsignedLong? = CUnsignedLong.max
-
-    // Preserve booleans
-    XCTAssertTrue((true as NSNumber).isTargetCastable(&bool))
-    XCTAssertTrue((true as NSNumber).isTargetCastable(&anyobj))
-    XCTAssertTrue((true as NSNumber).isTargetCastable(&nsnumber))
-    XCTAssertFalse((true as NSNumber).isTargetCastable(&int))
-    XCTAssertFalse((true as NSNumber).isTargetCastable(&float))
-    XCTAssertFalse((true as NSNumber).isTargetCastable(&long))
-
-    // Preserved size
-    XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int8))
-    XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int16))
-    XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int32))
-    XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int64))
-    XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&int))
-    XCTAssertTrue(NSNumber(char: int8!).isTargetCastable(&long))
-    XCTAssertFalse(NSNumber(char: int8!).isTargetCastable(&float))
-    XCTAssertFalse(NSNumber(char: int8!).isTargetCastable(&double))
-
-    XCTAssertFalse(NSNumber(short: int16!).isTargetCastable(&int8))
-    XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&int16))
-    XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&int32))
-    XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&int64))
-    XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&int))
-    XCTAssertTrue(NSNumber(short: int16!).isTargetCastable(&long))
-    XCTAssertFalse(NSNumber(short: int16!).isTargetCastable(&float))
-    XCTAssertFalse(NSNumber(short: int16!).isTargetCastable(&double))
-
-    XCTAssertFalse(NSNumber(int: int32!).isTargetCastable(&int8))
-    XCTAssertFalse(NSNumber(int: int32!).isTargetCastable(&int16))
-    XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&int32))
-    XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&int64))
-    XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&int))
-    XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&uint))
-    XCTAssertTrue(NSNumber(int: int32!).isTargetCastable(&long))
-    XCTAssertFalse(NSNumber(int: int32!).isTargetCastable(&float))
-    XCTAssertFalse(NSNumber(int: int32!).isTargetCastable(&double))
-
-    if sizeof(CLong) == sizeof(Int64) {
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int8))
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int16))
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int32))
-      XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&int64))
-      XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&long))
-      XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&int))
-      XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&uint))
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&float))
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&double))
-    } else {
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int8))
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&int16))
-      XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&int32))
-      XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&int64))
-      XCTAssertTrue(NSNumber(long: int!).isTargetCastable(&long))
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&float))
-      XCTAssertFalse(NSNumber(long: int!).isTargetCastable(&double))
-    }
-
-    XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&int8))
-    XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&int16))
-    XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&int32))
-    if sizeof(Int) == sizeof(Int64) {
-      XCTAssertTrue(NSNumber(longLong: int64!).isTargetCastable(&int))
-    } else {
-      XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&int))
-    }
-    XCTAssertTrue(NSNumber(longLong: int64!).isTargetCastable(&int64))
-    XCTAssertTrue(NSNumber(longLong: int64!).isTargetCastable(&long))
-    XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&float))
-    XCTAssertFalse(NSNumber(longLong: int64!).isTargetCastable(&double))
-
-    XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int8))
-    XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int16))
-    XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int32))
-    XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&uint8))
-    XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&uint16))
-    XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&uint32))
-    XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&uint64))
-    XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&ulong))
-    // TODO(zinman): Be smarter about when we allow this or not
-    if sizeof(Int) == sizeof(Int64) {
-      XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int))
-    } else {
-      XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int))
-    }
-    XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&int64))
-    XCTAssertTrue(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&long))
-    XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&float))
-    XCTAssertFalse(NSNumber(unsignedLongLong: uint64!).isTargetCastable(&double))
-
-    XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int8))
-    XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int16))
-    XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int32))
-    XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int64))
-    XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&int))
-    XCTAssertFalse(NSNumber(float: float!).isTargetCastable(&long))
-    XCTAssertTrue(NSNumber(float: float!).isTargetCastable(&float))
-    XCTAssertTrue(NSNumber(float: float!).isTargetCastable(&double))
-
-    XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&int8))
-    XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&int16))
-    XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&int32))
-    XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&int64))
-    XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&long))
-    XCTAssertFalse(NSNumber(double: double!).isTargetCastable(&float))
-    XCTAssertTrue(NSNumber(double: double!).isTargetCastable(&double))
-  }
-}