Making Swift's BootstrapTypes SyncbaseConvertible

This allows direct Puts and Gets in Syncbase using VOM version 0x80.
The following Swift types are supported: Bool, Int, Int8, Int16, Int32,
Int63, UInt, UInt8, UInt16, UInt32, UInt64, Float, Double, String,
NSData, [UInt8], [String]

This CL thereby enables Android compatibility on the data layer for all
VOM Bootstrap types.

This CL contains the bulk of the work to make Android and iOS work
together, but we still need to:
- update the data model on both sides to use only basic VOM types, which
  means that we have to update Android's TODO to use the same JSON-
  encoding as iOS (DiceRoller gets updated in this CL)
- verify that the inter-platform connectivity works as intended and
  update the mountpoints to point to the same namespaces in iOS&Android

Change-Id: I87cd041059ff13036e0b9fe9d5bf5f3ead2ab2d0
diff --git a/Syncbase/Example/Dice Roller/DiceViewController.swift b/Syncbase/Example/Dice Roller/DiceViewController.swift
index 446f68a..70af857 100644
--- a/Syncbase/Example/Dice Roller/DiceViewController.swift
+++ b/Syncbase/Example/Dice Roller/DiceViewController.swift
@@ -53,9 +53,11 @@
       .last?
       .value
     if let value = lastValue {
-      // Get the single byte out via typecasting to an array of UInt8. This will be unnecessary
-      // when we have VOM support in Swift.
-      currentDieRoll = unsafeBitCast(value.bytes, UnsafePointer<UInt8>.self).memory
+      do {
+        currentDieRoll = try UInt8.deserializeFromSyncbase(value)
+      } catch {
+        print("Unexpected error: \(error)")
+      }
       numberLabel.text = currentDieRoll.description
       UIApplication.sharedApplication().networkActivityIndicatorVisible = false
     }
@@ -72,15 +74,11 @@
       nextNum = UInt8(arc4random_uniform(6) + 1)
     } while (nextNum == currentDieRoll)
 
-    // Right now we can only store NSData, so we have to serialize this number to store it.
-    // Soon we will have VOM support and can just put the raw int as the value and expect
-    // it to work properly.
-    let value = NSData(bytes: &nextNum, length: 1)
     do {
       UIApplication.sharedApplication().networkActivityIndicatorVisible = true
-      try collection?.put(rowKey, value: value)
-    } catch let e {
-      print("Unexpected error: \(e)")
+      try collection?.put(rowKey, value: nextNum)
+    } catch {
+      print("Unexpected error: \(error)")
     }
   }
 
diff --git a/Syncbase/Source/Collection.swift b/Syncbase/Source/Collection.swift
index 121738d..ece4d19 100644
--- a/Syncbase/Source/Collection.swift
+++ b/Syncbase/Source/Collection.swift
@@ -61,6 +61,11 @@
     }
   }
 
+  // We support basic data types and [UInt8] and [String] in Swift's Collection.Put.
+  // Since Swift currently does not allow us to specify SyncbaseConvertible as the protocol for
+  // arrays with specific key types, we provide manual overrides for these type array types.
+  // Basic types are handled by a generic 'put' that takes SyncbaseConvertible.
+
   /// Puts `value` for `key`, overwriting any existing value. This call is idempotent, meaning
   /// you can safely call it multiple times in a row with the same values.
   public func put<T: SyncbaseCore.SyncbaseConvertible>(key: String, value: T) throws {
@@ -69,6 +74,22 @@
     }
   }
 
+  /// Puts `value` for `key`, overwriting any existing value. This call is idempotent, meaning
+  /// you can safely call it multiple times in a row with the same values.
+  public func put(key: String, value: [UInt8]) throws {
+    try SyncbaseError.wrap {
+      try self.coreCollection.put(key, value: value)
+    }
+  }
+
+  /// Puts `value` for `key`, overwriting any existing value. This call is idempotent, meaning
+  /// you can safely call it multiple times in a row with the same values.
+  public func put(key: String, value: [String]) throws {
+    try SyncbaseError.wrap {
+      try self.coreCollection.put(key, value: value)
+    }
+  }
+
   /// Deletes the value associated with `key`. If the row does not exist for the associated key,
   /// this call is a no-op. That is to say, `delete` is idempotent.
   public func delete(key: String) throws {
diff --git a/Syncbase/Source/Error.swift b/Syncbase/Source/Error.swift
index 9b498f6..71942cf 100644
--- a/Syncbase/Source/Error.swift
+++ b/Syncbase/Source/Error.swift
@@ -25,6 +25,8 @@
   case InvalidPermissionsChange
   case Exist
   case NoExist
+  case SerializationError(detail: String)
+  case DeserializationError(detail: String)
   case InvalidName(name: String)
   case CorruptDatabase(path: String)
   case InvalidOperation(reason: String)
@@ -58,6 +60,8 @@
     case .CastError(let obj): self = .CastError(obj: obj)
     case .IllegalArgument(let detail): self = .IllegalArgument(detail: detail)
     case .NoAccess(let detail): self = .NoAccess(detail: detail)
+    case .SerializationError(let detail): self = .SerializationError(detail: detail)
+    case .DeserializationError(let detail): self = .DeserializationError(detail: detail)
     case .UnknownVError(let err): self = .UnknownVError(err: err)
     }
   }
@@ -105,6 +109,8 @@
     case .CastError(let obj): return "Unable to convert to cast: \(obj)"
     case .IllegalArgument(let detail): return "Illegal argument: \(detail)"
     case .NoAccess(let detail): return "Access Denied: \(detail)"
+    case .SerializationError(let detail): return "Serialization Error: \(detail)"
+    case .DeserializationError(let detail): return "Deserialization Error: \(detail)"
     case .UnknownVError(let err): return "Unknown error: \(err)"
     }
   }
diff --git a/SyncbaseCore/Source/Collection.swift b/SyncbaseCore/Source/Collection.swift
index f426f51..dc4934d 100644
--- a/SyncbaseCore/Source/Collection.swift
+++ b/SyncbaseCore/Source/Collection.swift
@@ -156,6 +156,11 @@
     return cBytes.extract() ?? NSData()
   }
 
+  // We support basic data types and [UInt8] and [String] in Swift's Collection.Put.
+  // Since Swift currently does not allow us to specify SyncbaseConvertible as the protocol for
+  // arrays with specific key types, we provide manual overrides for these type array types.
+  // Basic types are handled by a generic 'put' that takes SyncbaseConvertible.
+
   /// Put writes the given value to this Collection under the given key.
   public func put(key: String, value: SyncbaseConvertible) throws {
     let data = try value.serializeToSyncbase()
@@ -168,6 +173,30 @@
     }
   }
 
+  /// Put writes the byte array to this Collection under the given key.
+  public func put(key: String, value: [UInt8]) throws {
+    let data = try value.serializeToSyncbase()
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_RowPut(
+        try encodedRowName(key),
+        try cBatchHandle(),
+        v23_syncbase_Bytes(data),
+        errPtr)
+    }
+  }
+
+  /// Put writes the String array to this Collection under the given key.
+  public func put(key: String, value: [String]) throws {
+    let data = try value.serializeToSyncbase()
+    try VError.maybeThrow { errPtr in
+      v23_syncbase_RowPut(
+        try encodedRowName(key),
+        try cBatchHandle(),
+        v23_syncbase_Bytes(data),
+        errPtr)
+    }
+  }
+
   /// Delete deletes the row for the given key.
   public func delete(key: String) throws {
     try VError.maybeThrow { errPtr in
diff --git a/SyncbaseCore/Source/Errors.swift b/SyncbaseCore/Source/Errors.swift
index 7cb3c1f..55e74ba 100644
--- a/SyncbaseCore/Source/Errors.swift
+++ b/SyncbaseCore/Source/Errors.swift
@@ -20,6 +20,8 @@
   case InvalidPermissionsChange
   case Exist
   case NoExist
+  case SerializationError(detail: String)
+  case DeserializationError(detail: String)
   case InvalidName(name: String)
   case CorruptDatabase(path: String)
   case InvalidOperation(reason: String)
@@ -76,6 +78,8 @@
     case .CastError(let obj): return "Unable to convert to cast: \(obj)"
     case .IllegalArgument(let detail): return "Illegal argument: \(detail)"
     case .NoAccess(let detail): return "Access Denied: \(detail)"
+    case .SerializationError(let detail): return "Serialization Error: \(detail)"
+    case .DeserializationError(let detail): return "Deserialization Error: \(detail)"
     case .UnknownVError(let err): return "Unknown error: \(err)"
     }
   }
diff --git a/SyncbaseCore/Source/Marshal.swift b/SyncbaseCore/Source/Marshal.swift
index fbe344e..f4d73b5 100644
--- a/SyncbaseCore/Source/Marshal.swift
+++ b/SyncbaseCore/Source/Marshal.swift
@@ -6,6 +6,10 @@
 
 import Foundation
 
+// The initial capacity fits the version byte, the type byte as well as up to 9 bytes to encode
+// doubles, float and the maximum Var128 value.
+let basicTypeInitialCapacity = 11
+
 public enum JsonErrors: ErrorType {
   case ArrayContainsInvalidTypes
   case DictionaryContainsInvalidTypes
@@ -52,17 +56,276 @@
   static func deserializeFromSyncbase<T: SyncbaseConvertible>(data: NSData) throws -> T
 }
 
+private func serializeType(wireType: VOM.WireType, data: [UInt8]? = nil) -> NSMutableData {
+  let buffer = NSMutableData(capacity: basicTypeInitialCapacity)!
+  buffer.appendBytes([VOM.encodingVersion], length: 1)
+  let type = VOM.binaryEncodeInt(wireType.rawValue)
+  buffer.appendBytes(type, length: type.count)
+  if let binaryData = data {
+    buffer.appendBytes(binaryData, length: binaryData.count)
+  }
+  return buffer
+}
+
+private func deserializeType(inout ptr: UnsafePointer<UInt8>, inout length: Int) throws -> VOM.WireType {
+  guard length > 1 && ptr[0] == VOM.encodingVersion else {
+    throw SyncbaseError.DeserializationError(detail: "Unsupported serialization version")
+  }
+
+  ptr = ptr.advancedBy(1); length -= 1
+  let encodedWireType = try VOM.binaryDecodeInt(ptr, available: length)
+  ptr = ptr.advancedBy(encodedWireType.bytesRead); length -= encodedWireType.bytesRead
+
+  guard let type = VOM.WireType(rawValue: encodedWireType.value) else {
+    throw SyncbaseError.DeserializationError(detail: "Invalid data type")
+  }
+
+  return type
+}
+
 extension SyncbaseConvertible {
   public static func deserializeFromSyncbase<T: SyncbaseConvertible>(data: NSData) throws -> T {
-    if let cast = data as? T {
-      return cast
+    var ptr = UnsafePointer<UInt8>(data.bytes)
+    var length = data.length
+    let wireType = try deserializeType(&ptr, length: &length)
+
+    switch wireType {
+    case VOM.WireType.Bool:
+      guard let result = try VOM.binaryDecodeBool(ptr, available: length).value as? T else {
+        throw SyncbaseError.CastError(obj: data)
+      }
+      return result
+    case VOM.WireType.Int8, VOM.WireType.Int16, VOM.WireType.Int32, VOM.WireType.Int64:
+      let value: Int64 = try VOM.binaryDecodeInt(ptr, available: length).value
+      // VOM allows conversions between certain compatible types. We mirror this here with conditional
+      // casts for integers.
+      if let result = Int64(value) as? T {
+        return result
+      } else if value >= Int64(Int.min) && value <= Int64(Int.max), let result = Int(value) as? T {
+        // Note: Not using ranges since the maximum Int64 can't be included inside of a range.
+        return result
+      } else if Int64(Int32.min)...Int64(Int32.max) ~= value, let result = Int32(value) as? T {
+        return result
+      } else if Int64(Int16.min)...Int64(Int16.max) ~= value, let result = Int16(value) as? T {
+        return result
+      } else if Int64(Int8.min)...Int64(Int8.max) ~= value, let result = Int8(value) as? T {
+        return result
+      }
+      throw SyncbaseError.CastError(obj: data)
+    case VOM.WireType.UInt8, VOM.WireType.UInt16, VOM.WireType.UInt32, VOM.WireType.UInt64:
+      var value: UInt64
+      if (wireType == VOM.WireType.UInt8) {
+        // In version 0x80, bytes are written directly as bytes to the stream.
+        // TODO(mrschmidt): Implement 0x81 decoding
+        value = UInt64(ptr[0])
+      } else {
+        value = try VOM.binaryDecodeUInt(ptr, available: length).value
+      }
+
+      // VOM allows conversions between certain compatible types. We mirror this here with conditional
+      // casts for unsigned integers.
+      if let result = UInt64(value) as? T {
+        return result
+      } else if value >= UInt64(UInt.min) && value <= UInt64(UInt.max), let result = UInt(value) as? T {
+        // Note: Not using ranges since the maximum UInt64 can't be included inside of a range.
+        return result
+      } else if UInt64(UInt32.min)...UInt64(UInt32.max) ~= value, let result = UInt32(value) as? T {
+        return result
+      } else if UInt64(UInt16.min)...UInt64(UInt16.max) ~= value, let result = UInt16(value) as? T {
+        return result
+      } else if UInt64(UInt8.min)...UInt64(UInt8.max) ~= value, let result = UInt8(value) as? T {
+        return result
+      }
+      throw SyncbaseError.CastError(obj: data)
+    case VOM.WireType.Float, VOM.WireType.Double:
+      let value: Double = try VOM.binaryDecodeDouble(ptr, available: length).value
+      // VOM allows conversions between certain compatible types. We mirror this here with conditional
+      // casts for floating point numbers.
+      if let result = Double(value) as? T {
+        return result
+      } else if let result = Float(value) as? T {
+        return result
+      }
+      throw SyncbaseError.CastError(obj: data)
+    case VOM.WireType.String:
+      guard let result = try VOM.binaryDecodeString(ptr, available: length).value as? T else {
+        throw SyncbaseError.CastError(obj: data)
+      }
+      return result
+    default:
+      throw SyncbaseError.DeserializationError(detail: "Unsupported data type")
     }
-    throw SyncbaseError.CastError(obj: data)
+  }
+}
+
+extension Bool: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.Bool, data: VOM.binaryEncodeBool(self))
+  }
+}
+
+extension Int: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.Int64, data: VOM.binaryEncodeInt(Int64(self)))
+  }
+}
+
+extension Int8: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.Int8, data: VOM.binaryEncodeInt(Int64(self)))
+  }
+}
+
+extension Int16: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.Int16, data: VOM.binaryEncodeInt(Int64(self)))
+  }
+}
+
+extension Int32: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.Int32, data: VOM.binaryEncodeInt(Int64(self)))
+  }
+}
+
+extension Int64: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.Int64, data: VOM.binaryEncodeInt(self))
+  }
+}
+
+extension UInt: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.UInt64, data: VOM.binaryEncodeUInt(UInt64(self)))
+  }
+}
+
+extension UInt8: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    // VOM 0x80 directly serializes bytes
+    return serializeType(VOM.WireType.UInt8, data: [self])
+  }
+}
+
+extension UInt16: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.UInt16, data: VOM.binaryEncodeUInt(UInt64(self)))
+  }
+}
+
+extension UInt32: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.UInt32, data: VOM.binaryEncodeUInt(UInt64(self)))
+  }
+}
+
+extension UInt64: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.UInt64, data: VOM.binaryEncodeUInt(self))
+  }
+}
+
+extension Float: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.Float, data: VOM.binaryEncodeDouble(Double(self)))
+  }
+}
+
+extension Double: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    return serializeType(VOM.WireType.Double, data: VOM.binaryEncodeDouble(self))
+  }
+}
+
+extension String: SyncbaseConvertible {
+  public func serializeToSyncbase() throws -> NSData {
+    let buffer = serializeType(VOM.WireType.String)
+
+    guard let data = VOM.binaryEncodeString(self) else {
+      throw SyncbaseError.SerializationError(detail: "Cannot convert String to UTF-8")
+    }
+
+    buffer.appendData(data)
+    return buffer
   }
 }
 
 extension NSData: SyncbaseConvertible {
   public func serializeToSyncbase() throws -> NSData {
-    return self
+    let buffer = serializeType(VOM.WireType.ByteList)
+    let length = VOM.binaryEncodeUInt(UInt64(self.length))
+    buffer.appendBytes(length, length: length.count)
+    buffer.appendBytes(self.bytes, length: self.length)
+    return buffer
+  }
+
+  public static func deserializeFromSyncbase(data: NSData) throws -> NSData {
+    let data: [UInt8] = try [UInt8].deserializeFromSyncbase(data)
+    return NSData(bytes: data, length: data.count)
   }
 }
+
+// In Swift2, extensions with constraints cannot specify a protocol, so instead of adherering
+// to SyncbaseConvertible, we provide custom functions that deal with [UInt8] and [String].
+extension _ArrayType where Generator.Element == UInt8 {
+  public func serializeToSyncbase() throws -> NSData {
+    let buffer = serializeType(VOM.WireType.ByteList)
+    let length = VOM.binaryEncodeUInt(UInt64(self.count))
+    buffer.appendBytes(length, length: length.count)
+    buffer.appendBytes([UInt8](self), length: self.count)
+    return buffer
+  }
+
+  public static func deserializeFromSyncbase(data: NSData) throws -> [UInt8] {
+    var ptr = UnsafePointer<UInt8>(data.bytes)
+    var length = data.length
+
+    guard try deserializeType(&ptr, length: &length) == VOM.WireType.ByteList else {
+      throw SyncbaseError.DeserializationError(detail: "Unsupported data type")
+    }
+    let listLength = try VOM.binaryDecodeUInt(ptr, available: length)
+    ptr = ptr.advancedBy(listLength.bytesRead); length -= listLength.bytesRead
+
+    guard Int(listLength.value) <= length else {
+      throw SyncbaseError.DeserializationError(detail: "Not enough data available")
+    }
+    let result = Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(ptr), count: Int(listLength.value)))
+    return result
+  }
+}
+
+extension _ArrayType where Generator.Element == String {
+  public func serializeToSyncbase() throws -> NSData {
+    let buffer = serializeType(VOM.WireType.StringList)
+    let length = VOM.binaryEncodeUInt(UInt64(self.count))
+    buffer.appendBytes(length, length: length.count)
+    for value in self {
+      guard let data = VOM.binaryEncodeString(value) else {
+        throw SyncbaseError.SerializationError(detail: "Cannot convert String to UTF-8")
+      }
+
+      buffer.appendData(data)
+    }
+    return buffer
+  }
+
+  public static func deserializeFromSyncbase(data: NSData) throws -> [String] {
+    var ptr = UnsafePointer<UInt8>(data.bytes)
+    var length = data.length
+
+    guard try deserializeType(&ptr, length: &length) == VOM.WireType.StringList else {
+      throw SyncbaseError.DeserializationError(detail: "Unsupported data type")
+    }
+
+    let listLength = try VOM.binaryDecodeUInt(ptr, available: length)
+    ptr = ptr.advancedBy(listLength.bytesRead); length -= listLength.bytesRead
+    var result = [String](count: Int(listLength.value), repeatedValue: "")
+    for i in 0..<Int(listLength.value) {
+      let stringValue = try VOM.binaryDecodeString(ptr, available: length)
+      ptr = ptr.advancedBy(stringValue.bytesRead); length -= stringValue.bytesRead
+      result[i] = stringValue.value
+    }
+    return result
+  }
+}
+
diff --git a/SyncbaseCore/Source/VOM.swift b/SyncbaseCore/Source/VOM.swift
new file mode 100644
index 0000000..6e8965f
--- /dev/null
+++ b/SyncbaseCore/Source/VOM.swift
@@ -0,0 +1,261 @@
+// 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
+
+typealias VomRawBytes = NSData
+
+/// VOM utility functions to encode and decode Bootstrap data types.
+/// This is mostly a port from binary_util.go.
+enum VOM {
+  static let encodingVersion = UInt8(0x80)
+
+  /// WireType is a mapping from Swift type name to their respective VDL constants as defined by
+  /// v.io/v23/vom/wiretype.vdl
+  enum WireType: Int64 {
+    case Bool = 1
+    case UInt8 = 2
+    case String = 3
+    case UInt16 = 4
+    case UInt32 = 5
+    case UInt64 = 6
+    case Int16 = 7
+    case Int32 = 8
+    case Int64 = 9
+    case Float = 10
+    case Double = 11
+    case Int8 = 16
+    case ByteList = 39
+    case StringList = 40
+  }
+
+  static let maxBufferSize = 9 // Maximum size for a Var128 encoded number
+
+  static func binaryEncodeBool(value: Bool) -> [UInt8] {
+    // Bools are encoded as a byte where 0 = false and anything else is true.
+    return [value ? 1 : 0]
+  }
+
+  typealias BoolWithEncodedLength = (value: Bool, bytesRead: Int)
+  static func binaryDecodeBool(bytes: UnsafePointer<UInt8>, available: Int) throws -> BoolWithEncodedLength {
+    guard available > 0 else {
+      throw SyncbaseError.DeserializationError(detail: "No or invalid data encountered")
+    }
+
+    return (bytes[0] == 1, 1)
+  }
+
+  static func binaryEncodeUInt(value: UInt64) -> [UInt8] {
+    // Unsigned integers are the basis for all other primitive values.  This is a
+    // two-state encoding.  If the number is less than 128 (0 through 0x7f), its
+    // value is written directly.  Otherwise the value is written in big-endian byte
+    // order preceded by the negated byte length.
+    switch value {
+    case 0 ... 0x7f:
+      var buffer = [UInt8](count: 1, repeatedValue: 0)
+      buffer[0] = UInt8(value)
+      return buffer
+    case 0x80...0xff:
+      var buffer = [UInt8](count: 2, repeatedValue: 0)
+      buffer[0] = UInt8(0xFF)
+      buffer[1] = UInt8(truncatingBitPattern: value)
+      return buffer
+    case 0x100...0xffff:
+      var buffer = [UInt8](count: 3, repeatedValue: 0)
+      buffer[0] = UInt8(0xFE)
+      buffer[1] = UInt8(truncatingBitPattern: value >> 8)
+      buffer[2] = UInt8(truncatingBitPattern: value)
+      return buffer
+    case 0x10000...0xffffff:
+      var buffer = [UInt8](count: 4, repeatedValue: 0)
+      buffer[0] = UInt8(0xFD)
+      buffer[1] = UInt8(truncatingBitPattern: value >> 16)
+      buffer[2] = UInt8(truncatingBitPattern: value >> 8)
+      buffer[3] = UInt8(truncatingBitPattern: value)
+      return buffer
+    case 0x1000000...0xffffffff:
+      var buffer = [UInt8](count: 5, repeatedValue: 0)
+      buffer[0] = UInt8(0xFC)
+      buffer[1] = UInt8(truncatingBitPattern: value >> 24)
+      buffer[2] = UInt8(truncatingBitPattern: value >> 16)
+      buffer[3] = UInt8(truncatingBitPattern: value >> 8)
+      buffer[4] = UInt8(truncatingBitPattern: value)
+      return buffer
+    case 0x100000000...0xffffffffff:
+      var buffer = [UInt8](count: 6, repeatedValue: 0)
+      buffer[0] = UInt8(0xFB)
+      buffer[1] = UInt8(truncatingBitPattern: value >> 32)
+      buffer[2] = UInt8(truncatingBitPattern: value >> 24)
+      buffer[3] = UInt8(truncatingBitPattern: value >> 16)
+      buffer[4] = UInt8(truncatingBitPattern: value >> 8)
+      buffer[5] = UInt8(truncatingBitPattern: value)
+      return buffer
+    case 0x10000000000...0xffffffffffff:
+      var buffer = [UInt8](count: 7, repeatedValue: 0)
+      buffer[0] = UInt8(0xFA)
+      buffer[1] = UInt8(truncatingBitPattern: value >> 40)
+      buffer[2] = UInt8(truncatingBitPattern: value >> 32)
+      buffer[3] = UInt8(truncatingBitPattern: value >> 24)
+      buffer[4] = UInt8(truncatingBitPattern: value >> 16)
+      buffer[5] = UInt8(truncatingBitPattern: value >> 8)
+      buffer[6] = UInt8(truncatingBitPattern: value)
+      return buffer
+    case 0x1000000000000...0xffffffffffffff:
+      var buffer = [UInt8](count: 8, repeatedValue: 0)
+      buffer[0] = UInt8(0xF9)
+      buffer[1] = UInt8(truncatingBitPattern: value >> 48)
+      buffer[2] = UInt8(truncatingBitPattern: value >> 40)
+      buffer[3] = UInt8(truncatingBitPattern: value >> 32)
+      buffer[4] = UInt8(truncatingBitPattern: value >> 24)
+      buffer[5] = UInt8(truncatingBitPattern: value >> 16)
+      buffer[6] = UInt8(truncatingBitPattern: value >> 8)
+      buffer[7] = UInt8(truncatingBitPattern: value)
+      return buffer
+    default:
+      var buffer = [UInt8](count: 9, repeatedValue: 0)
+      buffer[0] = UInt8(0xF8)
+      buffer[1] = UInt8(truncatingBitPattern: value >> 56)
+      buffer[2] = UInt8(truncatingBitPattern: value >> 48)
+      buffer[3] = UInt8(truncatingBitPattern: value >> 40)
+      buffer[4] = UInt8(truncatingBitPattern: value >> 32)
+      buffer[5] = UInt8(truncatingBitPattern: value >> 24)
+      buffer[6] = UInt8(truncatingBitPattern: value >> 16)
+      buffer[7] = UInt8(truncatingBitPattern: value >> 8)
+      buffer[8] = UInt8(truncatingBitPattern: value)
+      return buffer
+    }
+  }
+
+  typealias UIntWithEncodedLength = (value: UInt64, bytesRead: Int)
+  static func binaryDecodeUInt(bytes: UnsafePointer<UInt8>, available: Int) throws -> UIntWithEncodedLength {
+    guard available > 0 else {
+      throw SyncbaseError.DeserializationError(detail: "No data available")
+    }
+
+    let firstByte = bytes[0]
+    // Handle single-byte encoding.
+    if firstByte <= 0x7f {
+      return (UInt64(firstByte), 1)
+    }
+
+    // Handle multi-byte encoding.
+    let typeLength = Int(~firstByte + 1)
+    guard typeLength >= 1 && typeLength <= maxBufferSize && typeLength <= available else {
+      throw SyncbaseError.DeserializationError(detail: "Invalid length encountered")
+    }
+
+    var result: UInt64 = 0
+    for pos in 1...typeLength {
+      result = result << 8 | UInt64(bitPattern: Int64(bytes[pos]))
+    }
+
+    return (result, 1 + typeLength)
+  }
+
+  static func binaryEncodeInt(value: Int64) -> [UInt8] {
+    // Signed integers are encoded as unsigned integers, where the low bit says
+    // whether to complement the other bits to recover the int.
+    var uvalue: UInt64
+    if value < 0 {
+      uvalue = UInt64(~value) << 1 | 1
+    } else {
+      uvalue = UInt64(value) << 1
+    }
+    return binaryEncodeUInt(uvalue)
+  }
+
+  typealias IntWithEncodedLength = (value: Int64, bytesRead: Int)
+  static func binaryDecodeInt(bytes: UnsafePointer<UInt8>, available: Int) throws -> IntWithEncodedLength {
+    guard available > 0 else {
+      throw SyncbaseError.DeserializationError(detail: "No data available")
+    }
+
+    let firstByte = bytes[0]
+    // Handle single-byte encoding.
+    if firstByte <= 0x7f {
+      // The least significant bit is used to differentiate positive from negative values.
+      if firstByte & 0x1 == 1 {
+        return (~Int64(firstByte >> 1), 1)
+      }
+      return (Int64(firstByte >> 1), 1)
+    }
+
+    // Handle multi-byte encoding.
+    let typeLength = Int(~firstByte + 1)
+    guard typeLength >= 1 && typeLength <= maxBufferSize && typeLength <= available else {
+      throw SyncbaseError.DeserializationError(detail: "Invalid length encountered")
+    }
+
+    var result: UInt64 = 0
+    for pos in 1...typeLength {
+      // Need to convert UInt8 to Int64 here, since UInt64's bitPattern constructor takes an Int64.
+      result = result << 8 | UInt64(bitPattern: Int64(bytes[pos]))
+    }
+
+    if result & 0x1 == 1 {
+      // The least significant bit is used to differentiate positive from negative values.
+      return (~Int64(result >> 1), 1 + typeLength)
+    }
+    return (Int64(result >> 1), 1 + typeLength)
+  }
+
+  /// Reverses the byte order in data and returns resulting UInt64.
+  private static func reverseBytes(data: UInt64) -> UInt64 {
+    return (data&0x00000000000000ff) << 56 |
+    (data&0x000000000000ff00) << 40 |
+    (data&0x0000000000ff0000) << 24 |
+    (data&0x00000000ff000000) << 8 |
+    (data&0x000000ff00000000) >> 8 |
+    (data&0x0000ff0000000000) >> 24 |
+    (data&0x00ff000000000000) >> 40 |
+    (data&0xff00000000000000) >> 56
+  }
+
+  static func binaryEncodeDouble(value: Double) -> [UInt8] {
+    // Floating point numbers are encoded as byte-reversed IEEE 754.
+    let ieee = value._toBitPattern()
+    // Manually unrolled byte-reversing (to decrease size of output value).
+    let unsignedValue = reverseBytes(ieee)
+    return binaryEncodeUInt(unsignedValue)
+  }
+
+  typealias DoubleWithEncodedLength = (value: Double, bytesRead: Int)
+  static func binaryDecodeDouble(bytes: UnsafePointer<UInt8>, available: Int) throws -> DoubleWithEncodedLength {
+    let unsignedValue = try binaryDecodeUInt(bytes, available: available)
+    // Manually unrolled byte-reversing.
+    let ieee = reverseBytes(unsignedValue.value)
+    return (Double._fromBitPattern(ieee), unsignedValue.bytesRead)
+  }
+
+  static func binaryEncodeString(value: String) -> NSData? {
+    // Strings are encoded as the byte count followed by uninterpreted bytes.
+    let buffer = NSMutableData()
+    let length = binaryEncodeUInt(UInt64(value.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)))
+    buffer.appendBytes(length, length: length.count)
+    if let data = value.dataUsingEncoding(NSUTF8StringEncoding) {
+      buffer.appendData(data)
+      return buffer
+    } else {
+      return nil
+    }
+  }
+
+  static func binaryDecodeString(bytes: UnsafePointer<UInt8>, available: Int) throws -> (value: String, bytesRead: Int) {
+    let typeLength = Int(try binaryDecodeUInt(bytes, available: available).value)
+
+    if typeLength == 0 {
+      return ("", 1)
+    }
+
+    guard typeLength + 1 <= available else {
+      throw SyncbaseError.DeserializationError(detail: "Not enough data available")
+    }
+
+    if let result = String(data: NSData(bytes: bytes + 1, length: typeLength), encoding: NSUTF8StringEncoding) {
+      return (result, typeLength + 1)
+    } else {
+      throw SyncbaseError.DeserializationError(detail: "Invalid UTF-8 Sequence")
+    }
+  }
+}
\ No newline at end of file
diff --git a/SyncbaseCore/Source/Vom.swift b/SyncbaseCore/Source/Vom.swift
deleted file mode 100644
index 48a05f2..0000000
--- a/SyncbaseCore/Source/Vom.swift
+++ /dev/null
@@ -1,7 +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
-
-typealias VomRawBytes = NSData
diff --git a/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj b/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
index 1a89b5e..028772f 100644
--- a/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
+++ b/SyncbaseCore/SyncbaseCore.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		06638D651D40387B006F3822 /* VOMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06638D641D40387B006F3822 /* VOMTests.swift */; };
+		06638D671D4044D5006F3822 /* MarshalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06638D661D4044D5006F3822 /* MarshalTests.swift */; };
 		30A1F52E1CE68465008FC205 /* BasicDatabaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1F52D1CE68465008FC205 /* BasicDatabaseTests.swift */; };
 		30AD2E0A1CDD506700A28A0C /* SyncbaseCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 30AD2DFF1CDD506700A28A0C /* SyncbaseCore.framework */; };
 		30AD2E311CDD508700A28A0C /* Batch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E1B1CDD508700A28A0C /* Batch.swift */; };
@@ -22,7 +24,7 @@
 		30AD2E3E1CDD508700A28A0C /* SyncbaseCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 30AD2E281CDD508700A28A0C /* SyncbaseCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		30AD2E3F1CDD508700A28A0C /* Syncbase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E291CDD508700A28A0C /* Syncbase.swift */; };
 		30AD2E401CDD508700A28A0C /* Syncgroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2A1CDD508700A28A0C /* Syncgroup.swift */; };
-		30AD2E441CDD508700A28A0C /* Vom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2E1CDD508700A28A0C /* Vom.swift */; };
+		30AD2E441CDD508700A28A0C /* VOM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2E1CDD508700A28A0C /* VOM.swift */; };
 		30AD2E451CDD508700A28A0C /* Watch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E2F1CDD508700A28A0C /* Watch.swift */; };
 		30AD2E801CDD569200A28A0C /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E791CDD569200A28A0C /* Logging.swift */; };
 		30AD2E811CDD569200A28A0C /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AD2E7A1CDD569200A28A0C /* Strings.swift */; };
@@ -56,6 +58,8 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
+		06638D641D40387B006F3822 /* VOMTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VOMTests.swift; sourceTree = "<group>"; };
+		06638D661D4044D5006F3822 /* MarshalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarshalTests.swift; sourceTree = "<group>"; };
 		30A1F52D1CE68465008FC205 /* BasicDatabaseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicDatabaseTests.swift; sourceTree = "<group>"; };
 		30AD2DFF1CDD506700A28A0C /* SyncbaseCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SyncbaseCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		30AD2E091CDD506700A28A0C /* SyncbaseCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SyncbaseCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -73,7 +77,7 @@
 		30AD2E281CDD508700A28A0C /* SyncbaseCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SyncbaseCore.h; sourceTree = "<group>"; };
 		30AD2E291CDD508700A28A0C /* Syncbase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Syncbase.swift; sourceTree = "<group>"; };
 		30AD2E2A1CDD508700A28A0C /* Syncgroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Syncgroup.swift; sourceTree = "<group>"; };
-		30AD2E2E1CDD508700A28A0C /* Vom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vom.swift; sourceTree = "<group>"; };
+		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>"; };
 		30AD2E791CDD569200A28A0C /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Logging.swift; path = util/Logging.swift; sourceTree = "<group>"; };
@@ -163,7 +167,7 @@
 				30AD2E271CDD508700A28A0C /* Stream.swift */,
 				30AD2E291CDD508700A28A0C /* Syncbase.swift */,
 				30AD2E2A1CDD508700A28A0C /* Syncgroup.swift */,
-				30AD2E2E1CDD508700A28A0C /* Vom.swift */,
+				30AD2E2E1CDD508700A28A0C /* VOM.swift */,
 				30AD2E2F1CDD508700A28A0C /* Watch.swift */,
 				30AD2E771CDD567D00A28A0C /* Util */,
 			);
@@ -177,6 +181,8 @@
 				93B3C8381D41D7B5005B20C8 /* BridgeTests.swift */,
 				30AD2E481CDD508D00A28A0C /* Info.plist */,
 				9374F6661D00FF68004ECE59 /* TestHelpers.swift */,
+				06638D641D40387B006F3822 /* VOMTests.swift */,
+				06638D661D4044D5006F3822 /* MarshalTests.swift */,
 			);
 			path = Tests;
 			sourceTree = "<group>";
@@ -345,7 +351,7 @@
 				30AD2E381CDD508700A28A0C /* Marshal.swift in Sources */,
 				30AD2E3D1CDD508700A28A0C /* Stream.swift in Sources */,
 				30AD2E321CDD508700A28A0C /* Blob.swift in Sources */,
-				30AD2E441CDD508700A28A0C /* Vom.swift in Sources */,
+				30AD2E441CDD508700A28A0C /* VOM.swift in Sources */,
 				30AD2E341CDD508700A28A0C /* Database.swift in Sources */,
 				30AD2E311CDD508700A28A0C /* Batch.swift in Sources */,
 			);
@@ -355,9 +361,11 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				06638D671D4044D5006F3822 /* MarshalTests.swift in Sources */,
 				30A1F52E1CE68465008FC205 /* BasicDatabaseTests.swift in Sources */,
 				93B3C8391D41D7B5005B20C8 /* BridgeTests.swift in Sources */,
 				9374F6681D00FFE5004ECE59 /* TestHelpers.swift in Sources */,
+				06638D651D40387B006F3822 /* VOMTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/SyncbaseCore/Tests/MarshalTests.swift b/SyncbaseCore/Tests/MarshalTests.swift
new file mode 100644
index 0000000..9f259d0
--- /dev/null
+++ b/SyncbaseCore/Tests/MarshalTests.swift
@@ -0,0 +1,372 @@
+// 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
+import SyncbaseCore
+
+class MarshalTests: XCTestCase {
+  func adheresToProtocol(value: SyncbaseConvertible) {
+  }
+
+  func testCompatibility() {
+    // This is a compile-time test that makes sure that all primitive types conform to
+    // SyncbaseConvertible.
+    adheresToProtocol(true)
+    adheresToProtocol(Int(0))
+    adheresToProtocol(Int8(0))
+    adheresToProtocol(Int16(0))
+    adheresToProtocol(Int32(0))
+    adheresToProtocol(Int64(0))
+    adheresToProtocol(UInt(0))
+    adheresToProtocol(UInt8(0))
+    adheresToProtocol(UInt16(0))
+    adheresToProtocol(UInt32(0))
+    adheresToProtocol(UInt64(0))
+    adheresToProtocol(Double(0))
+    adheresToProtocol(Float(0))
+    adheresToProtocol(String(""))
+  }
+
+  func testBool() throws {
+    for value in [true, false] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: Bool = try Bool.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testInt() throws {
+    for value in [Int.min, 0, Int.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: Int = try Int.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testUInt() throws {
+    for value in [UInt.min, 0, UInt.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: UInt = try UInt.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testInt8() throws {
+    for value in [Int8.min, 0, Int8.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: Int8 = try Int8.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testUInt8() throws {
+    for value in [UInt8.min, 0, UInt8.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: UInt8 = try UInt8.deserializeFromSyncbase(serializedData)
+      // UInt8 are always encoded as one byte, but we also encode the version and type byte.
+      XCTAssertEqual(serializedData.length, 3)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testInt16() throws {
+    for value in [Int16.min, 0, Int16.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: Int16 = try Int16.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testUInt16() throws {
+    for value in [UInt16.min, 0, UInt16.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: UInt16 = try UInt16.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testInt32() throws {
+    for value in [Int32.min, 0, Int32.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: Int32 = try Int32.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testUInt32() throws {
+    for value in [UInt32.min, 0, UInt32.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: UInt32 = try UInt32.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testInt64() throws {
+    for value in [Int64.min, 0, Int64.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: Int64 = try Int64.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testInt64ToInt8() throws {
+    for value in [Int8.min, 0, Int8.max] {
+      let serializedData = try Int64(value).serializeToSyncbase()
+      let deserializedValue: Int8 = try Int8.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int8(value))
+    }
+  }
+
+  func testInt64ToInt16() throws {
+    for value in [Int16.min, 0, Int16.max] {
+      let serializedData = try Int64(value).serializeToSyncbase()
+      let deserializedValue: Int16 = try Int16.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int16(value))
+    }
+  }
+
+  func testInt64ToInt32() throws {
+    for value in [Int32.min, 0, Int32.max] {
+      let serializedData = try Int64(value).serializeToSyncbase()
+      let deserializedValue: Int32 = try Int32.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int32(value))
+    }
+  }
+
+  func testInt32ToInt8() throws {
+    for value in [Int8.min, 0, Int8.max] {
+      let serializedData = try Int32(value).serializeToSyncbase()
+      let deserializedValue: Int8 = try Int8.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int8(value))
+    }
+  }
+
+  func testInt32ToInt16() throws {
+    for value in [Int16.min, 0, Int16.max] {
+      let serializedData = try Int32(value).serializeToSyncbase()
+      let deserializedValue: Int16 = try Int16.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int16(value))
+    }
+  }
+
+  func testInt32ToInt64() throws {
+    for value in [Int32.min, 0, Int32.max] {
+      let serializedData = try Int32(value).serializeToSyncbase()
+      let deserializedValue: Int64 = try Int64.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int64(value))
+    }
+  }
+
+  func testInt16ToInt8() throws {
+    for value in [Int8.min, 0, Int8.max] {
+      let serializedData = try Int16(value).serializeToSyncbase()
+      let deserializedValue: Int8 = try Int8.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int8(value))
+    }
+  }
+
+  func testInt16ToInt32() throws {
+    for value in [Int16.min, 0, Int16.max] {
+      let serializedData = try Int16(value).serializeToSyncbase()
+      let deserializedValue: Int32 = try Int32.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int32(value))
+    }
+  }
+
+  func testInt16ToInt64() throws {
+    for value in [Int16.min, 0, Int16.max] {
+      let serializedData = try Int16(value).serializeToSyncbase()
+      let deserializedValue: Int64 = try Int64.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, Int64(value))
+    }
+  }
+
+  func testUInt64() throws {
+    for value in [UInt64.min, 0, UInt64.max] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: UInt64 = try UInt64.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testUInt64ToUInt8() throws {
+    for value in [UInt8.min, 0, UInt8.max] {
+      let serializedData = try UInt64(value).serializeToSyncbase()
+      let deserializedValue: UInt8 = try UInt8.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt8(value))
+    }
+  }
+
+  func testUInt64ToUInt16() throws {
+    for value in [UInt16.min, 0, UInt16.max] {
+      let serializedData = try UInt64(value).serializeToSyncbase()
+      let deserializedValue: UInt16 = try UInt16.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt16(value))
+    }
+  }
+
+  func testUInt64ToUInt32() throws {
+    for value in [UInt32.min, 0, UInt32.max] {
+      let serializedData = try UInt64(value).serializeToSyncbase()
+      let deserializedValue: UInt32 = try UInt32.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt32(value))
+    }
+  }
+
+  func testUInt32ToUInt8() throws {
+    for value in [UInt8.min, 0, UInt8.max] {
+      let serializedData = try UInt32(value).serializeToSyncbase()
+      let deserializedValue: UInt8 = try UInt8.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt8(value))
+    }
+  }
+
+  func testUInt32ToUInt16() throws {
+    for value in [UInt16.min, 0, UInt16.max] {
+      let serializedData = try UInt32(value).serializeToSyncbase()
+      let deserializedValue: UInt16 = try UInt16.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt16(value))
+    }
+  }
+
+  func testUInt32ToUInt64() throws {
+    for value in [UInt32.min, 0, UInt32.max] {
+      let serializedData = try UInt32(value).serializeToSyncbase()
+      let deserializedValue: UInt64 = try UInt64.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt64(value))
+    }
+  }
+
+  func testUInt16ToUInt8() throws {
+    for value in [UInt8.min, 0, UInt8.max] {
+      let serializedData = try UInt16(value).serializeToSyncbase()
+      let deserializedValue: UInt8 = try UInt8.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt8(value))
+    }
+  }
+
+  func testUInt16ToUInt32() throws {
+    for value in [UInt16.min, 0, UInt16.max] {
+      let serializedData = try UInt16(value).serializeToSyncbase()
+      let deserializedValue: UInt32 = try UInt32.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt32(value))
+    }
+  }
+
+  func testUInt16ToUInt64() throws {
+    for value in [UInt16.min, 0, UInt16.max] {
+      let serializedData = try UInt16(value).serializeToSyncbase()
+      let deserializedValue: UInt64 = try UInt64.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, UInt64(value))
+    }
+  }
+
+  func testFloat() throws {
+    let value = Float(13.17)
+    let serializedData = try value.serializeToSyncbase()
+    let deserializedValue: Float = try Float.deserializeFromSyncbase(serializedData)
+    XCTAssertEqual(deserializedValue, value)
+  }
+
+  func testDouble() throws {
+    let value = Double(13.17)
+    let serializedData = try value.serializeToSyncbase()
+    let deserializedValue: Double = try Double.deserializeFromSyncbase(serializedData)
+    XCTAssertEqual(deserializedValue, value)
+  }
+
+  func testDoubleToFloat() throws {
+    let value = Double(13.17)
+    let serializedData = try value.serializeToSyncbase()
+    let deserializedValue: Float = try Float.deserializeFromSyncbase(serializedData)
+    XCTAssertEqual(deserializedValue, Float(value))
+  }
+
+  func testString() throws {
+    for value in ["", "Hello world", "你好,世界", "😊"] {
+      let serializedData = try value.serializeToSyncbase()
+      let deserializedValue: String = try String.deserializeFromSyncbase(serializedData)
+      XCTAssertEqual(deserializedValue, value)
+    }
+  }
+
+  func testNSData() throws {
+    let data: [UInt8] = [0, 1, 2, 3, 255]
+    let value = NSData(bytes: data, length: data.count)
+    let serializedData = try value.serializeToSyncbase()
+    let deserializedValue: NSData = try NSData.deserializeFromSyncbase(serializedData)
+    XCTAssertEqual(deserializedValue, value)
+  }
+
+  func testUInt8List() throws {
+    let value: [UInt8] = [0, 1, 2, 3, 255]
+    let serializedData = try value.serializeToSyncbase()
+    let deserializedValue: [UInt8] = try [UInt8].deserializeFromSyncbase(serializedData)
+    XCTAssertEqual(deserializedValue, value)
+  }
+
+  func testUInt8EmptyList() throws {
+    let value: [UInt8] = []
+    let serializedData = try value.serializeToSyncbase()
+    let deserializedValue: [UInt8] = try [UInt8].deserializeFromSyncbase(serializedData)
+    XCTAssertEqual(deserializedValue, value)
+  }
+
+  func testStringList() throws {
+    let value = ["", "Hello world", "你好,世界", "😊"]
+    let serializedData = try value.serializeToSyncbase()
+    let deserializedValue: [String] = try [String].deserializeFromSyncbase(serializedData)
+    XCTAssertEqual(deserializedValue, value)
+  }
+
+  func testStringEmptyList() throws {
+    let value: [String] = []
+    let serializedData = try value.serializeToSyncbase()
+    let deserializedValue: [String] = try [String].deserializeFromSyncbase(serializedData)
+    XCTAssertEqual(deserializedValue, value)
+  }
+
+  func testErrorUIntValueOfRange() throws {
+    let value: UInt32 = UInt32(UInt16.max) + 1
+    let serializedData = try value.serializeToSyncbase()
+    do {
+      let _: UInt16 = try UInt16.deserializeFromSyncbase(serializedData)
+    } catch SyncbaseError.CastError {
+      return
+    }
+    XCTFail()
+  }
+
+  func testErrorIntValueOfRange() throws {
+    let value: Int32 = Int32(Int16.max) + 1
+    let serializedData = try value.serializeToSyncbase()
+    do {
+      let _: Int16 = try Int16.deserializeFromSyncbase(serializedData)
+    } catch SyncbaseError.CastError {
+      return
+    }
+    XCTFail()
+  }
+
+  func testErrorErrorWrongType() throws {
+    let value = "Hello world"
+    let serializedData = try value.serializeToSyncbase()
+    do {
+      let _: Int16 = try Int16.deserializeFromSyncbase(serializedData)
+    } catch SyncbaseError.CastError {
+      return
+    }
+    XCTFail()
+  }
+
+  func testErrorInvalidData() throws {
+    let serializedData = NSData(bytes: [UInt8(0)], length: 1)
+    do {
+      let _: Int16 = try Int16.deserializeFromSyncbase(serializedData)
+    } catch SyncbaseError.DeserializationError {
+      return
+    }
+    XCTFail()
+  }
+}
diff --git a/SyncbaseCore/Tests/TestHelpers.swift b/SyncbaseCore/Tests/TestHelpers.swift
index 03a954b..32d04e1 100644
--- a/SyncbaseCore/Tests/TestHelpers.swift
+++ b/SyncbaseCore/Tests/TestHelpers.swift
@@ -26,6 +26,40 @@
   "Admin": AccessList(allowed: [anyPermissions], notAllowed: []),
   "Read": AccessList(allowed: [anyPermissions], notAllowed: [])]
 
+/// Convert NSData to its hex representation.
+func dataToHexString(data: NSData) -> String {
+  let buf = UnsafePointer<UInt8>(data.bytes)
+
+  func base16(value: UInt8) -> UInt8 {
+    let charA = UInt8(UnicodeScalar("a").value)
+    let char0 = UInt8(UnicodeScalar("0").value)
+    return (value > 9) ? (charA + value - 10) : (char0 + value)
+  }
+
+  let ptr = UnsafeMutablePointer<UInt8>.alloc(data.length * 2)
+  for i in 0 ..< data.length {
+    ptr[i * 2] = base16((buf[i] >> 4) & 0xF)
+    ptr[i * 2 + 1] = base16(buf[i] & 0xF)
+  }
+
+  return String(bytesNoCopy: ptr, length: data.length * 2, encoding: NSASCIIStringEncoding, freeWhenDone: true)!
+}
+
+/// Convert a Hex String to NSData.
+func hexStringToData(hex: String) -> NSData {
+  let result = NSMutableData()
+
+  var startIndex = hex.startIndex
+  for _ in 0..<hex.characters.count / 2 {
+    let endIndex = startIndex.advancedBy(2)
+    let singleByte = UInt8(hex[startIndex..<endIndex], radix: 16)!
+    result.appendBytes([singleByte], length: 1)
+    startIndex = endIndex
+  }
+
+  return result
+}
+
 /// Convert integer seconds into Grand Central Dispatch (GCD)'s dispatch_time_t format.
 func secondsGCD(seconds: Int64) -> dispatch_time_t {
   return dispatch_time(DISPATCH_TIME_NOW, seconds * Int64(NSEC_PER_SEC))
diff --git a/SyncbaseCore/Tests/VOMTests.swift b/SyncbaseCore/Tests/VOMTests.swift
new file mode 100644
index 0000000..4cb5757
--- /dev/null
+++ b/SyncbaseCore/Tests/VOMTests.swift
@@ -0,0 +1,148 @@
+// 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 XCTest
+@testable import SyncbaseCore
+
+class VOMTest: XCTestCase {
+  func testBinaryEncodeDecodeBool() throws {
+    let testValues: [Bool: String] = {
+      var testValues = [Bool: String]()
+      testValues[false] = "00"
+      testValues[true] = "01"
+      return testValues
+    }()
+
+    for (boolEncoding, hexEncoding) in testValues {
+      let data = VOM.binaryEncodeBool(boolEncoding)
+      XCTAssertEqual(dataToHexString(NSData(bytes: data, length: data.count)), hexEncoding)
+    }
+
+    for (boolEncoding, hexEncoding) in testValues {
+      let inputData = hexStringToData(hexEncoding)
+      XCTAssertEqual(try VOM.binaryDecodeBool(UnsafePointer<UInt8>(inputData.bytes), available: inputData.length).value,
+        boolEncoding)
+    }
+  }
+
+  func testBinaryEncodeDecodeUInt64() throws {
+    let testValues: [UInt64: String] = {
+      var testValues = [UInt64: String]()
+      testValues[UInt64(0)] = "00"
+      testValues[UInt64(1)] = "01"
+      testValues[UInt64(2)] = "02"
+      testValues[UInt64(127)] = "7f"
+      testValues[UInt64(128)] = "ff80"
+      testValues[UInt64(255)] = "ffff"
+      testValues[UInt64(256)] = "fe0100"
+      testValues[UInt64(257)] = "fe0101"
+      testValues[UInt64(0xffff)] = "feffff"
+      testValues[UInt64(0xffffff)] = "fdffffff"
+      testValues[UInt64(0xffffffff)] = "fcffffffff"
+      testValues[UInt64(0xffffffffff)] = "fbffffffffff"
+      testValues[UInt64(0xffffffffffff)] = "faffffffffffff"
+      testValues[UInt64(0xffffffffffffff)] = "f9ffffffffffffff"
+      return testValues
+    }()
+
+    for (integerEncoding, hexEncoding) in testValues {
+      let data = VOM.binaryEncodeUInt(integerEncoding)
+      XCTAssertEqual(dataToHexString(NSData(bytes: data, length: data.count)), hexEncoding)
+    }
+
+    for (integerEncoding, hexEncoding) in testValues {
+      let inputData = hexStringToData(hexEncoding)
+      XCTAssertEqual(try VOM.binaryDecodeUInt(UnsafePointer<UInt8>(inputData.bytes), available: inputData.length).value,
+        integerEncoding)
+    }
+  }
+
+  func testBinaryEncodeDecodeInt64() throws {
+    let testValues: [Int64: String] = {
+      var testValues = [Int64: String]()
+      testValues[Int64(0)] = "00"
+      testValues[Int64(1)] = "02"
+      testValues[Int64(2)] = "04"
+      testValues[Int64(63)] = "7e"
+      testValues[Int64(64)] = "ff80"
+      testValues[Int64(65)] = "ff82"
+      testValues[Int64(127)] = "fffe"
+      testValues[Int64(128)] = "fe0100"
+      testValues[Int64(129)] = "fe0102"
+      testValues[Int64(Int16.max)] = "fefffe"
+      testValues[Int64(Int32.max)] = "fcfffffffe"
+      testValues[Int64(Int64.max)] = "f8fffffffffffffffe"
+      testValues[Int64(-1)] = "01"
+      testValues[Int64(-2)] = "03"
+      testValues[Int64(-64)] = "7f"
+      testValues[Int64(-65)] = "ff81"
+      testValues[Int64(-66)] = "ff83"
+      testValues[Int64(-128)] = "ffff"
+      testValues[Int64(-129)] = "fe0101"
+      testValues[Int64(-130)] = "fe0103"
+      testValues[Int64(Int16.min)] = "feffff"
+      testValues[Int64(Int32.min)] = "fcffffffff"
+      testValues[Int64(Int64.min)] = "f8ffffffffffffffff"
+      return testValues
+    }()
+
+    for (integerEncoding, hexEncoding) in testValues {
+      let data = VOM.binaryEncodeInt(integerEncoding)
+      XCTAssertEqual(dataToHexString(NSData(bytes: data, length: data.count)), hexEncoding)
+    }
+
+    for (integerEncoding, hexEncoding) in testValues {
+      let inputData = hexStringToData(hexEncoding)
+      XCTAssertEqual(try VOM.binaryDecodeInt(UnsafePointer<UInt8>(inputData.bytes), available: inputData.length).value,
+        integerEncoding)
+    }
+  }
+
+  func testBinaryEncodeDecodeDouble() throws {
+    let testValues: [Double: String] = {
+      var testValues = [Double: String]()
+      testValues[Double(0)] = "00"
+      testValues[Double(1)] = "fef03f"
+      testValues[Double(17)] = "fe3140"
+      testValues[Double(18)] = "fe3240"
+      return testValues
+    }()
+
+    for (doubleEncoding, hexEncoding) in testValues {
+      let data = VOM.binaryEncodeDouble(doubleEncoding)
+      XCTAssertEqual(dataToHexString(NSData(bytes: data, length: data.count)), hexEncoding)
+    }
+
+    for (doubleEncoding, hexEncoding) in testValues {
+      let inputData = hexStringToData(hexEncoding)
+      XCTAssertEqual(try VOM.binaryDecodeDouble(UnsafePointer<UInt8>(inputData.bytes), available: inputData.length).value,
+        doubleEncoding)
+    }
+  }
+
+  func testBinaryEncodeDecodeString() throws {
+    let testValues: [String: String] = {
+      var testValues = [String: String]()
+      testValues[""] = "00"
+      testValues["abc"] = "03616263"
+      testValues["defghi"] = "06646566676869"
+      testValues["你好,世界"] = "0fe4bda0e5a5bdefbc8ce4b896e7958c"
+      testValues["😊"] = "04f09f988a"
+      return testValues
+    }()
+
+    for (stringEnconding, hexEncoding) in testValues {
+      let data = VOM.binaryEncodeString(stringEnconding)
+      XCTAssertEqual(dataToHexString(data!), hexEncoding)
+    }
+
+    for (stringEnconding, hexEncoding) in testValues {
+      let inputData = hexStringToData(hexEncoding)
+      XCTAssertEqual(try VOM.binaryDecodeString(UnsafePointer<UInt8>(inputData.bytes), available: inputData.length).value,
+        stringEnconding)
+    }
+  }
+}
+