Skip to content

Commit

Permalink
refactor: random bytes generation; data and string extensions update (#…
Browse files Browse the repository at this point in the history
…791)

* Update HexDecodable+Extensions.swift

* Update Data+Extension.swift

* Update String+Extension.swift

* chore: refactoring + documentation for Data.fromHex function

* updates as per conversations

* chore: updated docs for randomBytes + minor refactoring

* chore: fixed typo in Data+Extension.swift

* chore: lint issue fixed

* add string spit test

* Revert "add string spit test" github issue

This reverts commit e8a5e2b.

---------

Co-authored-by: Jenea Vranceanu <[email protected]>
Co-authored-by: Jenea Vranceanu <[email protected]>
  • Loading branch information
3 people authored Nov 7, 2023
1 parent 2d4bffd commit bab86ca
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ extension BigInt: LiteralInitiableFromString { }
extension BigUInt: LiteralInitiableFromString { }

extension Data: LiteralInitiableFromString {
/// Converts hexadecimal string representation of some bytes into actual bytes.
/// Notes:
/// - empty string will return `nil`;
/// - empty hex string, meaning it's equal to `"0x"`, will return empty `Data` object.
/// - Parameter hex: bytes represented as string.
/// - Returns: optional raw bytes.
public static func fromHex(_ hex: String) -> Data? {
let string = hex.lowercased().stripHexPrefix()
let array = [UInt8](hex: string)
if array.count == 0 {
if hex == "0x" || hex == "" {
return Data()
} else {
return nil
}
}
return Data(array)
let hex = hex.lowercased().trim()
guard !hex.isEmpty else { return nil }
guard hex != "0x" else { return Data() }
let bytes = [UInt8](hex: hex.stripHexPrefix())
return bytes.isEmpty ? nil : Data(bytes)
}
}
39 changes: 21 additions & 18 deletions Sources/Web3Core/Utility/Data+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

import Foundation

extension Data {
public extension Data {

init<T>(fromArray values: [T]) {
let values = values
let ptrUB = values.withUnsafeBufferPointer { (ptr: UnsafeBufferPointer) in return ptr }
Expand Down Expand Up @@ -33,32 +34,34 @@ extension Data {
return difference == UInt8(0x00)
}

public static func zero(_ data: inout Data) {
static func zero(_ data: inout Data) {
let count = data.count
data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in
body.baseAddress?.assumingMemoryBound(to: UInt8.self).initialize(repeating: 0, count: count)
}
}

public static func randomBytes(length: Int) -> Data? {
for _ in 0...1024 {
var data = Data(repeating: 0, count: length)
let result = data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) -> Int32? in
if let bodyAddress = body.baseAddress, body.count > 0 {
let pointer = bodyAddress.assumingMemoryBound(to: UInt8.self)
return SecRandomCopyBytes(kSecRandomDefault, length, pointer)
} else {
return nil
}
}
if let notNilResult = result, notNilResult == errSecSuccess {
return data
}
/**
Generates an array of random bytes of the specified length.
This function uses `SecRandomCopyBytes` to generate random bytes returning it as a `Data` object.
If an error occurs during random bytes generation, the function returns `nil`.
Error occurs only if `SecRandomCopyBytes` returns status that is not `errSecSuccess`.
See [all status codes](https://developer.apple.com/documentation/security/1542001-security_framework_result_codes) for possible error reasons.
Note: in v4 of web3swift this function will be deprecated and a new implementation will be provided that will throw occurred error.
- Parameter length: The number of random bytes to generate.

- Returns: optional `Data` object containing the generated random bytes, or `nil` if an error occurred during generation.
*/
static func randomBytes(length: Int) -> Data? {
var entropyBytes = [UInt8](repeating: 0, count: length)
let status = SecRandomCopyBytes(kSecRandomDefault, entropyBytes.count, &entropyBytes)
guard status == errSecSuccess else {
return nil
}
return nil
return Data(entropyBytes)
}

public func bitsInRange(_ startingBit: Int, _ length: Int) -> UInt64? { // return max of 8 bytes for simplicity, non-public
func bitsInRange(_ startingBit: Int, _ length: Int) -> UInt64? { // return max of 8 bytes for simplicity, non-public
if startingBit + length / 8 > self.count, length > 64, startingBit > 0, length >= 1 { return nil }
let bytes = self[(startingBit/8) ..< (startingBit+length+7)/8]
let padding = Data(repeating: 0, count: 8 - bytes.count)
Expand Down
20 changes: 20 additions & 0 deletions Sources/Web3Core/Utility/String+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,26 @@ extension String {
func trim() -> String {
trimmingCharacters(in: .whitespacesAndNewlines)
}

/// Splits a string into groups of `every` n characters, grouping from left-to-right by default. If `backwards` is true, right-to-left.
public func split(every: Int, backwards: Bool = false) -> [String] {
var result = [String]()

for i in stride(from: 0, to: self.count, by: every) {
switch backwards {
case true:
let endIndex = self.index(self.endIndex, offsetBy: -i)
let startIndex = self.index(endIndex, offsetBy: -every, limitedBy: self.startIndex) ?? self.startIndex
result.insert(String(self[startIndex..<endIndex]), at: 0)
case false:
let startIndex = self.index(self.startIndex, offsetBy: i)
let endIndex = self.index(startIndex, offsetBy: every, limitedBy: self.endIndex) ?? self.endIndex
result.append(String(self[startIndex..<endIndex]))
}
}

return result
}
}

extension Character {
Expand Down
11 changes: 11 additions & 0 deletions Tests/web3swiftTests/localTests/UncategorizedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ class UncategorizedTests: XCTestCase {
XCTAssert(biguint == BigUInt("126978086000000000"))
}

func testStringSplit() {
XCTAssertEqual("abcdefgh".split(every: 3), ["abc", "def", "gh"])
XCTAssertEqual("abcdefgh".split(every: 3, backwards: true), ["ab", "cde", "fgh"])

XCTAssertEqual("abcdefgh".split(every: 10), ["abcdefgh"])
XCTAssertEqual("".split(every: 3), [])

XCTAssertEqual("abcdefgh".split(every: 1), ["a", "b", "c", "d", "e", "f", "g", "h"])
XCTAssertEqual("abcdefgh".split(every: 1, backwards: true), ["a", "b", "c", "d", "e", "f", "g", "h"]) // should be the same as from the front
}

func testBloom() throws {
let positive = [
"testtest",
Expand Down

0 comments on commit bab86ca

Please sign in to comment.