Skip to content

Instantly share code, notes, and snippets.

@brennanMKE
Last active July 8, 2022 05:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save brennanMKE/99f5e42e74f56bf3958522edd4b01cca to your computer and use it in GitHub Desktop.
Save brennanMKE/99f5e42e74f56bf3958522edd4b01cca to your computer and use it in GitHub Desktop.
Bytes formatted for binary or hex strings

Bytes formatted for binary or hex strings

Visualizing bytes when working with numbers is a useful way to quickly inspect values. The original version of this code used a brute force implementation where each of the unsigned integer types in Swift were given computed properties for bytes and hex which was a lot of repeated code. Looking back at this code and reading through Apple's docs a bit more I discovered there is a property for bitWidth which eliminated the need to hard-code this value. It was also not necessary to always pass a UInt64 typed value into the formatted function. There is also a protocol named UnsignedInteger which includes all of these types. But creating a protocol extension it was possible to add a small bit of code once which is applied to all types. A lot of code was eliminated and it may also be more memory efficient using the type it already was instead of casting every value to UInt64.

The revised version is much simpler and half the code.

Below is the output.

UInt8
Bits: 00000000, Hex: 00
Bits: 00000001, Hex: 01
Bits: 00000011, Hex: 03
Bits: 00000111, Hex: 07
Bits: 00010000, Hex: 10
Bits: 00011000, Hex: 18
Bits: 00100000, Hex: 20
Bits: 11111111, Hex: FF
UInt32
Bits: 00000000000000000000000000000000, Hex: 00000000
Bits: 00000000000000000000000000000001, Hex: 00000001
Bits: 00000000000000000000000000000011, Hex: 00000003
Bits: 00000000000000000000000000000111, Hex: 00000007
Bits: 00000000000000000000000000010000, Hex: 00000010
Bits: 00000000000000000000000000011000, Hex: 00000018
Bits: 00000000000000000000000000100000, Hex: 00000020
Bits: 00000000000000001111111111111111, Hex: 0000FFFF
Bits: 11111111111111111111111111111111, Hex: FFFFFFFF
UInt64
Bits: 0000000000000000000000000000000000000000000000000000000000000000, Hex: 0000000000000000
Bits: 0000000000000000000000000000000000000000000000000000000000000001, Hex: 0000000000000001
Bits: 0000000000000000000000000000000000000000000000000000000000000011, Hex: 0000000000000003
Bits: 0000000000000000000000000000000000000000000000000000000000000111, Hex: 0000000000000007
Bits: 0000000000000000000000000000000000000000000000000000000000010000, Hex: 0000000000000010
Bits: 0000000000000000000000000000000000000000000000000000000000011000, Hex: 0000000000000018
Bits: 0000000000000000000000000000000000000000000000000000000000100000, Hex: 0000000000000020
Bits: 0000000000000000000000000000000011111111111111111111111111111111, Hex: 00000000FFFFFFFF
Bits: 1111111111111111111111111111111111111111111111111111111111111111, Hex: FFFFFFFFFFFFFFFF
// Original
import Foundation
struct Bytes {
enum Size: Int {
case eight = 8
case sixteen = 16
case thirtyTwo = 32
case sixtyFour = 64
var length: Int {
return rawValue
}
}
enum Kind: Int {
case binary = 2
case hex = 16
var radix: Int {
return rawValue
}
}
static func formatted(number: UInt64, size: Bytes.Size, kind: Bytes.Kind) -> String {
let bits = String(number, radix: kind.radix).uppercased()
let length = kind == .binary ? size.length : size.length / 4
let count = length - bits.count
let padding = String(repeating: "0", count: count)
let result = padding + bits
return result
}
}
extension UInt8 {
var bytes: String {
return Bytes.formatted(number: UInt64(self), size: .eight, kind: .binary)
}
var hex: String {
return Bytes.formatted(number: UInt64(self), size: .eight, kind: .hex)
}
}
extension UInt16 {
var bytes: String {
return Bytes.formatted(number: UInt64(self), size: .sixteen, kind: .binary)
}
var hex: String {
return Bytes.formatted(number: UInt64(self), size: .sixteen, kind: .hex)
}
}
extension UInt32 {
var bytes: String {
return Bytes.formatted(number: UInt64(self), size: .thirtyTwo, kind: .binary)
}
var hex: String {
return Bytes.formatted(number: UInt64(self), size: .thirtyTwo, kind: .hex)
}
}
extension UInt64 {
var bytes: String {
return Bytes.formatted(number: self, size: .sixtyFour, kind: .binary)
}
var hex: String {
return Bytes.formatted(number: self, size: .sixtyFour, kind: .hex)
}
}
// Revised
import Foundation
struct Bytes {
enum Format: Int {
case binary = 2
case hex = 16
var radix: Int {
rawValue
}
}
}
extension UnsignedInteger {
var bytes: String {
pad(format: .binary)
}
var hex: String {
pad(format: .hex)
}
func pad(format: Bytes.Format) -> String {
let bits = String(self, radix: format.radix, uppercase: true)
let length = format == .binary ? bitWidth : bitWidth / 4
let count = length - bits.count
let padding = String(repeating: "0", count: count)
let result = padding + bits
return result
}
}
let bytes8: [UInt8] = [UInt8.min, 1, 3, 7, 16, 24, 32, UInt8.max]
bytes8.forEach { byte in
print("Bits: \(byte.bytes), Hex: \(byte.hex)")
}
let bytes32: [UInt32] = [UInt32.min, 1, 3, 7, 16, 24, 32, UInt32(UInt16.max), UInt32.max]
bytes32.forEach { byte in
print("Bits: \(byte.bytes), Hex: \(byte.hex)")
}
let bytes64: [UInt64] = [UInt64.min, 1, 3, 7, 16, 24, 32, UInt64(UInt32.max), UInt64.max]
bytes64.forEach { byte in
print("Bits: \(byte.bytes), Hex: \(byte.hex)")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment