Building structure for binary file

Hey,

I really don’t know where to post stupid questions… so here we are:
I’m currently building a file parser and I’m looking for a way to represent different data structures within a datastream.

I got the basic functionality working and can parse the given file but my code is all over the place and not well maintainable. The problem that I have is, that Swift Dicts can’t have different types so I’m relying on a pattern like [String: Any] to represent different binaries like UInt32, UInt16 and Strings.

Currently using an ugly mix of switch to parse different font formats.

Is there a better way? Like using a struct, enum, class? (Unfortunately I think I need a way to iterate over the properties(?)) Maybe Pointers are the solution, but I digged not into it.

Disclosure: I’m really new to all that binary stuff and bitwise stuff (also not a pro in swift). Anyway, I hope I expressed my self in a way you can understand my problem.

Best,

G

EDIT:

I’m now looking into Decodable and a custom Decoder...

EDIT 2:

BinaryCodable seems like the right way.. maybe

You can do it in a more type-safe way, like this:

enum Number {
     case integer (Int)
     case natural (UInt)
     case real    (Double)
     case bool    (Bool)
}

var zeros = [String : Number] ()

zeros ["Z"] = .integer (0)
zeros ["N"] = .natural (0)
zeros ["R"] = .real (0)
zeros ["B"] = .bool(false)

for (k, v) in zeros {
    print (k, v)
}

// prints
Z integer(0)
N natural(0)
B bool(false)
R real(0.0)
1 Like

Looks good.
Usage would be like:

enum FileHeader {
  case signature (UInt32)
  case length (UInt32)
  case offset (UInt16)
  ...
}

Thats right? I’ll give it a try.

Edit:
Ah sorry. You mean to represent the structure as a dict in a typesafe way...

Edit 2:
@ibex10 But accessing the value is complicated, isn't it?

I'm now doing something like this:

enum BinaryType {
    case uint32 (UInt32)
    case uint16 (UInt16)
    case uint8 (UInt8)
    case string (String)
    
    var string: String? {
        switch self {
        case let .string(val):
            return val
        default:
            return nil
        }
    }
    
    var uint32: UInt32? {
        switch self {
            
        case let .uint32(val):
            return val
        case let .uint16(val):
            return UInt32(val)
        case let .uint8(val):
            return UInt32(val)
        case .string:
            return nil
        }
    }
}

But that seems odd...

Edit 3:
Found that syntax – looks fancy..

guard case .uint32(let val) = enumVar else { return nil }

// do something with val

I think you meant this:

struct FileHeader {
    let signature : UInt32
    let length    : UInt32
    let offset    : UInt16
    ...
}
1 Like

I meant your idea with enum but I missed the point..

The problem with using a struct is, that I cant programatically fill the struct based on the properties, because the files are structured differently.

But maybe I miss something.. what I really looking for is the best practice to decode a binary file into structured data which I can access easily. This solutions are just my simple ideas, because I don't know anything about that, I think.

A similar topic has been discussed here.

1 Like

Got a good solution with just using structs for the structure.
Thanks!