Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DSL Style for Byte Array Creation #4

Merged
merged 8 commits into from Feb 7, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 44 additions & 0 deletions Sources/BitWiser/DSL/ByteArrayBuilder.swift
@@ -0,0 +1,44 @@
//
// ByteArrayBuilder.swift
//
//
// Created by Andrea Finollo on 30/01/22.
//

import Foundation


/// `ByteArrayBuilder` is a `@resultBuilder` to create sequence of `Byte` in a DSL style
@resultBuilder
public enum ByteArrayBuilder {

public static func buildBlock(_ components: [Byte]...) -> [Byte] {
components.flatMap { $0 }
}

public static func buildExpression(_ expression: [Byte]) -> [Byte] {
expression
}

public static func buildExpression(_ expression: Byte) -> [Byte] {
[expression]
}

public static func buildOptional(_ component: [Byte]?) -> [Byte] {
component ?? []
}

public static func buildEither(first component: [Byte]) -> [Byte] {
component
}

public static func buildEither(second component: [Byte]) -> [Byte] {
component
}

public static func buildArray(_ components: [[Byte]]) -> [Byte] {
return components.flatMap { $0 }
}


}
55 changes: 55 additions & 0 deletions Sources/BitWiser/DSL/DataConvertibleBuilder.swift
@@ -0,0 +1,55 @@
//
// DataConvertibleBuilder.swift
//
//
// Created by Andrea Finollo on 04/02/22.
//

import Foundation

/// `DataConvertibleBuilder` is a `@resultBuilder` to create `Data` in a DSL style
@resultBuilder
public enum DataConvertibleBuilder {

public static func buildBlock(_ components: DataConvertible...) -> Data {
return components.reduce(Data()) { partialResult, value in
var mutBuffer = partialResult
mutBuffer.append(value.data)
return mutBuffer
}
}

public static func buildExpression(_ expression: [DataConvertible]) -> Data {
return expression.reduce(Data()) { partialResult, value in
var mutBuffer = partialResult
mutBuffer.append(value.data)
return mutBuffer
}
}

public static func buildExpression(_ expression: DataConvertible) -> Data {
expression.data
}

public static func buildOptional(_ component: DataConvertible?) -> Data {
return component?.data ?? Data()
}

public static func buildEither(first component: DataConvertible) -> Data {
return component.data
}

public static func buildEither(second component: DataConvertible) -> Data {
return component.data
}

public static func buildArray(_ components: [DataConvertible]) -> Data {
return components
.reduce(Data()) { partialResult, value in
var mutBuffer = partialResult
mutBuffer.append(value.data)
return mutBuffer
}
}

}
18 changes: 18 additions & 0 deletions Sources/BitWiser/DataRepresentable/DataRepresentable.swift
@@ -0,0 +1,18 @@
//
// DataRepresentable.swift
//
//
// Created by Andrea Finollo on 04/02/22.
//

import Foundation

public protocol ExpressibleByData {
init?(data: Data)
}

public protocol DataConvertible {
var data: Data { get }
}

public typealias DataRepresentable = ExpressibleByData & DataConvertible
31 changes: 31 additions & 0 deletions Sources/BitWiser/Extension/Extension+Data.swift
Expand Up @@ -18,6 +18,17 @@ extension Data: ByteRepresentable {
}
}

extension Data : DataConvertible {

public init?(data: Data) {
self.init(data)
}

public var data: Data {
return self
}
}

public extension Data {

/// Option about how to encode the hex string representation
Expand All @@ -44,3 +55,23 @@ public extension Data {
}.joined(separator: padding)
}
}

public extension Data {

/// Initialize a `Data` with a `@DataConvertibleBuilder`.
///
/// Data {
/// [Byte(0x00)]
/// Byte(0x01)
/// 0x02
/// "\u{03}"
/// CustomObject()
/// }
///
/// - parameter representables: A DSL closure with `DataRepresentable`s. Object passed in the closure must conform to `DataRepresentable` protocol.
/// - Note: Objects passed in the closure can have different `Data` lenght.
/// - Important: Always start from the LSB.
init(@DataConvertibleBuilder _ representables: () -> Data) {
self.init(representables())
}
}
45 changes: 45 additions & 0 deletions Sources/BitWiser/Extension/Extension+Numeric.swift
Expand Up @@ -7,6 +7,34 @@

import Foundation

extension DataConvertible where Self: ExpressibleByIntegerLiteral{
public init?(data: Data) {
var value: Self = 0
guard data.count == MemoryLayout.size(ofValue: value) else { return nil }
_ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}

public var data: Data {
return withUnsafeBytes(of: self) { Data($0) }
}
}

extension Int8: DataConvertible { }
extension Int16: DataConvertible { }
extension Int32: DataConvertible { }
extension Int64: DataConvertible { }
extension Int: DataConvertible { }

extension Float: DataConvertible { }
extension Double: DataConvertible { }

extension UInt8: DataConvertible { }
extension UInt16: DataConvertible { }
extension UInt32: DataConvertible { }
extension UInt64: DataConvertible { }
extension UInt: DataConvertible { }

// MARK: - Signed Numeric

extension Int16: ByteConvertible {
Expand Down Expand Up @@ -103,3 +131,20 @@ extension Array where Element == Byte {
return or
}
}

public extension Array where Element == Byte {
/// Initialize a `[Byte]`Array with a `@ByteArrayBuilder`.
///
/// Array<Byte> {
/// [Byte(0x00)]
/// Byte(0x01)
/// 0x02
/// [UInt8(0x03)]
/// }
///
/// - parameter builder: A DSL closure with `Byte`s.
/// - Important: Always start from the LSB
init(@ByteArrayBuilder _ builder: () -> [Byte]) {
self.init(builder())
}
}
12 changes: 12 additions & 0 deletions Sources/BitWiser/Extension/Extension+String.swift
Expand Up @@ -7,6 +7,18 @@

import Foundation

extension String: DataConvertible {

public init?(data: Data) {
self.init(data: data, encoding: .utf8)
}

public var data: Data {
// Note: a conversion to UTF-8 cannot fail.
return Data(self.utf8)
}
}

public extension String {
/// Given a hex string it returns a Data
/// - Returns: a `Data` value
Expand Down
2 changes: 1 addition & 1 deletion Tests/BitWiserTests/ByteTests.swift
Expand Up @@ -16,7 +16,7 @@ class ByteTests: XCTestCase {
var hexDescription = value.hexDescription
XCTAssertTrue(binDescription == "00000000")
XCTAssertTrue(hexDescription == "00")

value = 0b11111111
binDescription = value.binaryDescription
hexDescription = value.hexDescription
Expand Down