Skip to content

Commit

Permalink
Added custom encoding and decoding factory. Better Compact and BigInt…
Browse files Browse the repository at this point in the history
… types
  • Loading branch information
ypopovych committed Jan 12, 2021
1 parent e7d7f1d commit 9ec93bb
Show file tree
Hide file tree
Showing 18 changed files with 507 additions and 414 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ Setup instructions:
Add this to the dependency section of your `Package.swift` manifest:

```Swift
.package(url: "https://github.com/tesseract-one/swift-scale-codec.git", from: "0.1.0")
.package(url: "https://github.com/tesseract-one/swift-scale-codec.git", from: "0.2.0")
```

- **CocoaPods:** Put this in your `Podfile`:

```Ruby
pod 'ScaleCodec', '~> 0.1'
pod 'ScaleCodec', '~> 0.2'
```

## Usage Examples
Expand All @@ -66,7 +66,7 @@ assert(uint32 == UInt32.max)

#### Compact encoding

`UInt[8-64]` and `BigUInt` types can be encoded with compact encoding. This allows `BigUInt` to store values up to `2^536-1`.
`UInt[8-64]`, `SUInt[128-512]` and `BigUInt` types can be encoded with compact encoding. This allows `BigUInt` to store values up to `2^536-1`.

ScaleCodec has special wrapper type `SCompact` which encodes and decodes values in this format and two helper methods.

Expand All @@ -77,7 +77,7 @@ import ScaleCodec

let data = Data([0x07, 0x00, 0x00, 0x00, 0x00, 0x01])

let encoded = try SCALE.default.encode(compact: UInt64(1 << 32))
let encoded = try SCALE.default.encode(UInt64(1 << 32), .compact)
assert(encoded == data))

let compact = try SCALE.default.decode(UInt64.self, .compact, from: data)
Expand All @@ -100,15 +100,15 @@ let data = Data([
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
])

let encoded = try SCALE.default.encode(b128: BigUInt(2).pow(128) - 1)
let encoded = try SCALE.default.encode(BigUInt(2).pow(128) - 1, .b128)
assert(encoded == data))

let compact = try SCALE.default.decode(BigUInt.self, .b128, from: data)
assert(compact == BigUInt(2).pow(128) - 1)

// without helper methods
// let encoded = try SCALE.default.encode(SUInt256(BigUInt(2).pow(128) - 1))
// let compact = try SCALE.default.decode(SUInt256.self, from: data).value
// let compact = try SCALE.default.decode(SUInt256.self, from: data).int
```

#### Data fixed encoding
Expand All @@ -120,7 +120,7 @@ import ScaleCodec

let data = Data([0x07, 0x00, 0x00, 0x00, 0x00, 0x01]

let encoded = try SCALE.default.encoder().encode(data, fixed: 6).output
let encoded = try SCALE.default.encoder().encode(data, .fixed(6)).output
assert(encoded == data))

let decoded = try SCALE.default.decoder(data: encoded).decode(Data.self, .fixed(6))
Expand Down Expand Up @@ -152,7 +152,7 @@ import ScaleCodec

let array: [UInt32] = [1, 2, 3, 4, 5]

let data = try SCALE.default.encode(array, fixed: 5)
let data = try SCALE.default.encode(array, .fixed(5))

let decoded: [UInt32] = try SCALE.default.decode(.fixed(5), from: data)

Expand Down Expand Up @@ -223,8 +223,8 @@ enum Test: ScaleCodable {

func encode(in encoder: ScaleEncoder) throws {
switch self {
case .A(let str): try encoder.encode(enumCaseId: 0).encode(str)
case .B(let int, let str): try encoder.encode(enumCaseId: 1).encode(compact: int).encode(str)
case .A(let str): try encoder.encode(0, .enumCaseId).encode(str)
case .B(let int, let str): try encoder.encode(1, .enumCaseId).encode(int, .compact).encode(str)
}
}
}
Expand Down Expand Up @@ -263,7 +263,7 @@ struct Test: ScaleCodable, Equatable {
func encode(in encoder: ScaleEncoder) throws {
try encoder
.encode(var1)
.encode(compact: var2)
.encode(var2, .compact)
.encode(var3.map { SCompact($0) })
}
}
Expand Down
2 changes: 1 addition & 1 deletion ScaleCodec.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'ScaleCodec'
s.version = '0.1.1'
s.version = '0.2.0'
s.summary = 'SCALE codec implementation for Swift language'

s.description = <<-DESC
Expand Down
139 changes: 60 additions & 79 deletions Sources/ScaleCodec/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extension Array: ScaleContainerEncodable {
public typealias EElement = Element

public func encode(in encoder: ScaleEncoder, writer: @escaping (EElement, ScaleEncoder) throws -> Void) throws {
try encoder.encode(compact: UInt32(count))
try encoder.encode(UInt32(count), .compact)
for element in self {
try writer(element, encoder)
}
Expand All @@ -36,96 +36,77 @@ extension Array: ScaleEncodable where Element: ScaleEncodable {}

extension Array: ScaleDecodable where Element: ScaleDecodable {}

extension ScaleDecoder {
public func decode<T: ScaleDecodable>(_ type: [T].Type, _ fixed: ScaleFixedTypeMarker) throws -> [T] {
return try self.decode(fixed)
}
public protocol ScaleArrayInitializable {
associatedtype IElement

public func decode<T>(
_ type: [T].Type, _ fixed: ScaleFixedTypeMarker,
reader: @escaping (ScaleDecoder) throws -> T
) throws -> [T] {
return try self.decode(fixed, reader: reader)
}
init(array: [IElement])
}

extension Array: ScaleArrayInitializable {
public typealias IElement = Element

public func decode<T>(
_ fixed: ScaleFixedTypeMarker,
reader: @escaping (ScaleDecoder) throws -> T
) throws -> [T] {
guard case .fixed(let size) = fixed else { fatalError() } // Compiler error silencing.
var values = Array<T>()
values.reserveCapacity(Int(size))
for _ in 0..<size {
try values.append(reader(self))
}
return values
public init(array: [IElement]) {
self.init(array)
}
}

public protocol ScaleArrayConvertible {
associatedtype CElement

var asArray: [CElement] { get }
}

extension Array: ScaleArrayConvertible {
public typealias CElement = Element

public func decode<T: ScaleDecodable>(_ fixed: ScaleFixedTypeMarker) throws -> [T] {
try self.decode(fixed) { decoder in try decoder.decode() }
public var asArray: [CElement] { return self }
}

extension ScaleCustomDecoderFactory where T: ScaleArrayInitializable, T.IElement: ScaleDecodable {
public static func fixed(_ size: UInt) -> ScaleCustomDecoderFactory {
.fixed(size) { decoder in try decoder.decode() }
}
}

extension ScaleEncoder {
@discardableResult
public func encode<T: ScaleEncodable>(_ values: [T], fixed: UInt) throws -> ScaleEncoder {
try self.encode(values, fixed: fixed) { val, enc in try enc.encode(val) }
return self
extension ScaleCustomDecoderFactory where T: ScaleArrayInitializable {
public static func fixed(
_ size: UInt, _ reader: @escaping (ScaleDecoder) throws -> T.IElement
) -> ScaleCustomDecoderFactory {
ScaleCustomDecoderFactory { decoder in
var values = Array<T.IElement>()
values.reserveCapacity(Int(size))
for _ in 0..<size {
try values.append(reader(decoder))
}
return T(array: values)
}
}

@discardableResult
public func encode<T>(
_ values: [T], fixed: UInt,
writer: @escaping (T, ScaleEncoder) throws -> Void
) throws -> ScaleEncoder {
guard values.count == fixed else {
throw SEncodingError.invalidValue(
values, SEncodingError.Context(
path: self.path,
description: "Wrong value count \(values.count) expected \(fixed)"
}

extension ScaleCustomEncoderFactory where T: ScaleArrayConvertible {
public static func fixed(
_ size: UInt, writer: @escaping (T.CElement, ScaleEncoder) throws -> Void
) -> ScaleCustomEncoderFactory {
ScaleCustomEncoderFactory { encoder, conv in
let values = conv.asArray
guard values.count == size else {
throw SEncodingError.invalidValue(
values, SEncodingError.Context(
path: encoder.path,
description: "Wrong value count \(values.count) expected \(size)"
)
)
)
}
for val in values {
try writer(val, self)
}
for val in values {
try writer(val, encoder)
}
return encoder
}
return self
}
}

extension SCALE {
public func decode<T: ScaleDecodable>(
_ type: [T].Type, _ fixed: ScaleFixedTypeMarker, from data: Data
) throws -> [T] {
return try self.decode(fixed, from: data)
}

public func decode<T>(
_ type: [T].Type, _ fixed: ScaleFixedTypeMarker, from data: Data,
reader: @escaping (ScaleDecoder) throws -> T
) throws -> [T] {
return try self.decode(fixed, from: data, reader: reader)
}

public func decode<T: ScaleDecodable>(_ fixed: ScaleFixedTypeMarker, from data: Data) throws -> [T] {
return try self.decoder(data: data).decode(fixed)
}

public func decode<T>(
_ fixed: ScaleFixedTypeMarker, from data: Data,
reader: @escaping (ScaleDecoder) throws -> T
) throws -> [T] {
return try self.decoder(data: data).decode(fixed, reader: reader)
}

public func encode<T: ScaleEncodable>(_ values: [T], fixed: UInt) throws -> Data {
return try self.encoder().encode(values, fixed: fixed).output
}

public func encode<T>(
_ values: [T], fixed: UInt,
writer: @escaping (T, ScaleEncoder) throws -> Void
) throws -> Data {
return try self.encoder().encode(values, fixed: fixed, writer: writer).output
extension ScaleCustomEncoderFactory where T: ScaleArrayConvertible, T.CElement: ScaleEncodable {
public static func fixed(_ size: UInt) -> ScaleCustomEncoderFactory {
.fixed(size) { val, enc in try enc.encode(val) }
}
}
Loading

0 comments on commit 9ec93bb

Please sign in to comment.